Copy link to clipboard
Copied
I would like to automate the process of adding colour chip layers to my images. The images will only ever contain between 2–16 colours, and are pixel-perfect. I would like to be able to automatically scan the flat layer containing the image, and create rectangle layers for each colour in the image resulting in something like the below:
I thought it could be possible using index colour to create a swatch palette, but not sure how I could then create the rectangles automatically using the colours in the saved palette?. Alternatively, can a script just scan the layer for every variant of colour since I can be sure there will only ever be max 16?
Is this possible? Or wishful thinking?
Copy link to clipboard
Copied
Hi, i think it's not possible this is complex work...regards
Copy link to clipboard
Copied
A layer can contains millions of pixels and be larger then your document canvas size, have any aspect ratio, shape and have millions of colors. Script processing is slow they are not compiled into machine code executing to process your image. Scripts are basically source code that is interpret when run by a script processor into machine code a statement at a time then executed. A script that process an image pixel by pixel is not a script you would want to use. For it could take hours to process and image layer.
If your image is an Index Color Image it would contain no more than 256 mapped colors. You may be able to program a Photoshop Script to process the document color table to create your swatches layer. It is easy to add an empty layer and set square selections and fill the selections with a solid color.
Copy link to clipboard
Copied
Thanks for your reply! I understand a layer can have millions of colours, but the layers I'm working with only ever contain between 2–16 so I thought it might be possible in my circumstances.
A script to process the document colour table to create the swatches layer sounds like the best option. I agree it is easy to add an empty layer and set square selections to fill with the solid colours (this is the method I'm using currently)—I'm just wondering if a script might be possible to automate this using the info from the colour table as it's such a repetitive task which I'm completing many times a day. Do you have any suggestions re. what else I could research a script to help with this? Thank you again!
Copy link to clipboard
Copied
I gave you the suggestions to use an index color image to limit the number of colors and the process the color table not the images pixels. The script would most likely need to use action manager code to retrieve the color table that needs to exist. You should use searches to see if you can find a script the process a index color table, and look in Adobes Photoshop JavaScript reference manual to see if there are methods to process color tables. If your documents will only have up to 16 colors the execution times should be acceptable.
Copy link to clipboard
Copied
Please provide one image and the intended resulting image.
If there are really only up to 16 colors it might be possible to use Histogram to get at the colors without the step of creating a Color Table but …?
Copy link to clipboard
Copied
It is not an elegant Script and with a 2048px by 2048px image it takes more than 80sec for me, but it creates Shape Layers for up to 30 colors.
Edit: The Script compares the possible combinations of the three Channel historgram values against the actual colors with a Layer set to Blend Mode »Difference« … so that takes time.
If the color areas are anti-aliased or blurred it naturally will not work.
// create shape layers for colors used in image up to 30;
// 2021, use it at your own risk;
if (app.documents.length > 0 && activeDocument.mode == DocumentMode.RGB) {theStuff ()};
function theStuff () {
var time1 = Number(timeString());
var myDocument = app.activeDocument.duplicate("theCopy", true);
// collect histograms and non-0-values;
var theHistos = new Array;
for (var m = 0; m < 3; m++) {
var histo = myDocument.channels[m].histogram;
var theValues = new Array;
for (var n = 0; n < histo.length; n++) {
if (histo[n] != 0) {theValues.push(n)}
};
theHistos.push([histo, theValues]);
};
if (theHistos[0][1].length > 30 ||theHistos[1][1].length > 30 || theHistos[2][1].length > 30) {
alert ("too many colors");
myDocument.close(SaveOptions.DONOTSAVECHANGES);
return
};
////////////////////////////////////
var theArray = new Array;
// create difference layer to determine which combinations of values exist more or less;
var theSolid = solidColorLayer (55, 128, 200, "difference", charIDToTypeID("Dfrn"));
var theCurveLayer = rgbCurvesLayer ([[[0,0], [4,255]], [[0,0], [4,255]], [[0,0], [4,255]]]);
myDocument.activeLayer = theSolid;
// check the histogram combos;
for (var n = 0; n < theHistos[0][1].length; n++) {
for (var o = 0; o < theHistos[1][1].length; o++) {
for (var p = 0; p < theHistos[2][1].length; p++) {
try {
changeSolidColorLayer (theHistos[0][1][n], theHistos[1][1][o], theHistos[2][1][p]);
//if (myDocument.histogram[0] > 0) {
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("histogram"));
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var docDesc = executeActionGet(ref);
var list = docDesc.getList(stringIDToTypeID("histogram"));
if (list.getInteger(0) > 0) {
theArray.push([theHistos[0][1][n], theHistos[1][1][o], theHistos[2][1][p]])
};
}
catch (e) {};
};
};
};
// remove layers;
theSolid.remove();
theCurveLayer.remove();
////////////////////////////////////
if (theArray.length > 30) {
alert ("more than 30 colors – too much colors");
myDocument.close(SaveOptions.DONOTSAVECHANGES);
return
};
var theSide = 75;
var theDist = 25;
rectangleShapeLayer ([theDist, theDist, theDist+(theSide+theDist)*theArray.length+theDist, theDist*3+theSide], 255, 255, 255, "white");
for (var b = 0; b < theArray.length; b++) {
var theX = theDist*2+theSide*b+theDist*b;
rectangleShapeLayer ([theX, theDist+theDist, theX+theSide, theDist*2+theSide], theArray[b][0], theArray[b][1], theArray[b][2], String(theArray[b][0]+"_"+theArray[b][1]+"_"+theArray[b][2]))
};
////////////////////////////////////
var time2 = Number(timeString());
alert(((time2-time1)/1000)+" seconds\nstart "+time1+"\nend "+time2)
};
////// create solid color layer //////
function solidColorLayer (theR, theG, theB, theName, theBlendMode) {
// =======================================================
var idMk = charIDToTypeID( "Mk " );
var desc10 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref1 = new ActionReference();
var idcontentLayer = stringIDToTypeID( "contentLayer" );
ref1.putClass( idcontentLayer );
desc10.putReference( idnull, ref1 );
var idUsng = charIDToTypeID( "Usng" );
var desc11 = new ActionDescriptor();
var idNm = charIDToTypeID( "Nm " );
desc11.putString( idNm, theName );
var idMd = charIDToTypeID( "Md " );
var idBlnM = charIDToTypeID( "BlnM" );
var idMltp = theBlendMode ;
desc11.putEnumerated( idMd, idBlnM, idMltp );
var idType = charIDToTypeID( "Type" );
var desc12 = new ActionDescriptor();
var idClr = charIDToTypeID( "Clr " );
var desc13 = new ActionDescriptor();
desc13.putDouble( charIDToTypeID( "Rd " ), theR );
desc13.putDouble( charIDToTypeID( "Grn " ), theG );
desc13.putDouble( charIDToTypeID( "Bl " ), theB );
var idRGBC = charIDToTypeID( "RGBC" );
desc12.putObject( idClr, idRGBC, desc13 );
var idsolidColorLayer = stringIDToTypeID( "solidColorLayer" );
desc11.putObject( idType, idsolidColorLayer, desc12 );
var idcontentLayer = stringIDToTypeID( "contentLayer" );
desc10.putObject( idUsng, idcontentLayer, desc11 );
executeAction( idMk, desc10, DialogModes.NO );
return app.activeDocument.activeLayer
};
////// changeSolidColor //////
function changeSolidColorLayer (theR, theG, theB) {
try {
// =======================================================
var idsetd = charIDToTypeID( "setd" );
var desc17 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
ref2.putEnumerated(stringIDToTypeID( "contentLayer" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ));
desc17.putReference( idnull, ref2 );
var idT = charIDToTypeID( "T " );
var desc18 = new ActionDescriptor();
var idClr = charIDToTypeID( "Clr " );
var desc19 = new ActionDescriptor();
desc19.putDouble( charIDToTypeID( "Rd " ), theR );
desc19.putDouble( charIDToTypeID( "Grn " ), theG );
desc19.putDouble( charIDToTypeID( "Bl " ), theB );
var idRGBC = charIDToTypeID( "RGBC" );
desc18.putObject( idClr, idRGBC, desc19 );
var idsolidColorLayer = stringIDToTypeID( "solidColorLayer" );
desc17.putObject( idT, idsolidColorLayer, desc18 );
executeAction( idsetd, desc17, DialogModes.NO );
return true
} catch (e) {return false}
};
////// function to get the date //////
function timeString () {
var now = new Date();
return now.getTime()
};
////// rectangle //////
function rectangleShapeLayer (theArray, theR, theG, theB, theName) {
// =======================================================
var idPxl = charIDToTypeID( "#Pxl" );
var idMk = charIDToTypeID( "Mk " );
var desc44 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref9 = new ActionReference();
var idcontentLayer = stringIDToTypeID( "contentLayer" );
ref9.putClass( idcontentLayer );
desc44.putReference( idnull, ref9 );
var idUsng = charIDToTypeID( "Usng" );
var desc45 = new ActionDescriptor();
var idType = charIDToTypeID( "Type" );
var desc46 = new ActionDescriptor();
var idClr = charIDToTypeID( "Clr " );
var desc47 = new ActionDescriptor();
desc47.putDouble( charIDToTypeID( "Rd " ), theR );
desc47.putDouble( charIDToTypeID( "Grn " ), theG );
desc47.putDouble( charIDToTypeID( "Bl " ), theB );
var idRGBC = charIDToTypeID( "RGBC" );
desc46.putObject( idClr, idRGBC, desc47 );
var idsolidColorLayer = stringIDToTypeID( "solidColorLayer" );
desc45.putObject( idType, idsolidColorLayer, desc46 );
var idShp = charIDToTypeID( "Shp " );
var desc48 = new ActionDescriptor();
var idunitValueQuadVersion = stringIDToTypeID( "unitValueQuadVersion" );
desc48.putInteger( idunitValueQuadVersion, 1 );
var idTop = charIDToTypeID( "Top " );
desc48.putUnitDouble( idTop, idPxl, theArray[1] );
var idLeft = charIDToTypeID( "Left" );
desc48.putUnitDouble( idLeft, idPxl, theArray[0] );
var idBtom = charIDToTypeID( "Btom" );
desc48.putUnitDouble( idBtom, idPxl, theArray[3] );
var idRght = charIDToTypeID( "Rght" );
desc48.putUnitDouble( idRght, idPxl, theArray[2] );
var idtopRight = stringIDToTypeID( "topRight" );
desc48.putUnitDouble( idtopRight, idPxl, 0.000000 );
var idtopLeft = stringIDToTypeID( "topLeft" );
desc48.putUnitDouble( idtopLeft, idPxl, 0.000000 );
var idbottomLeft = stringIDToTypeID( "bottomLeft" );
desc48.putUnitDouble( idbottomLeft, idPxl, 0.000000 );
var idbottomRight = stringIDToTypeID( "bottomRight" );
desc48.putUnitDouble( idbottomRight, idPxl, 0.000000 );
var idRctn = charIDToTypeID( "Rctn" );
desc45.putObject( idShp, idRctn, desc48 );
desc44.putObject( idUsng, idcontentLayer, desc45 );
var idLyrI = charIDToTypeID( "LyrI" );
desc44.putInteger( idLyrI, 5 );
executeAction( idMk, desc44, DialogModes.NO );
// =======================================================
var idset = stringIDToTypeID( "set" );
var idlayer = stringIDToTypeID( "layer" );
var desc18 = new ActionDescriptor();
var ref6 = new ActionReference();
ref6.putEnumerated( idlayer, stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc18.putReference( stringIDToTypeID( "null" ), ref6 );
var desc19 = new ActionDescriptor();
desc19.putString( stringIDToTypeID( "name" ), theName );
desc18.putObject( stringIDToTypeID( "to" ), idlayer, desc19 );
executeAction( idset, desc18, DialogModes.NO );
};
////// check for selection //////
function hasSelection(){
var ref10 = new ActionReference();
ref10.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
ref10.putEnumerated( charIDToTypeID( "Dcmn" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
var docDesc = executeActionGet(ref10);
return docDesc.hasKey(stringIDToTypeID("selection"));
};
////// make rgb curves layer //////
function rgbCurvesLayer (theArray) {
// =======================================================
var idMk = charIDToTypeID( "Mk " );
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindDefault = stringIDToTypeID( "presetKindDefault" );
var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
var idadjustmentLayer = stringIDToTypeID( "adjustmentLayer" );
var idordinal = stringIDToTypeID( "ordinal" );
var idtargetEnum = stringIDToTypeID( "targetEnum" );
var idadjustment = stringIDToTypeID( "adjustment" );
var idcurvesAdjustment = stringIDToTypeID( "curvesAdjustment" );
var idcurves = stringIDToTypeID( "curves" );
var idcurve = stringIDToTypeID( "curve" );
var idchannel = stringIDToTypeID( "channel" );
var idAdjL = charIDToTypeID( "AdjL" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
var idChnl = charIDToTypeID( "Chnl" );
var idnull = charIDToTypeID( "null" );
var idCrvs = charIDToTypeID( "Crvs" );
var idCrvA = charIDToTypeID( "CrvA" );
var idCrv = charIDToTypeID( "Crv " );
var idAdjs = charIDToTypeID( "Adjs" );
var desc5 = new ActionDescriptor();
var ref2 = new ActionReference();
ref2.putClass( idAdjL );
desc5.putReference( idnull, ref2 );
var idUsng = charIDToTypeID( "Usng" );
var desc6 = new ActionDescriptor();
var idType = charIDToTypeID( "Type" );
var desc7 = new ActionDescriptor();
desc7.putEnumerated( idpresetKind, idpresetKindType, idpresetKindDefault );
desc6.putObject( idType, idCrvs, desc7 );
desc5.putObject( idUsng, idAdjL, desc6 );
executeAction( idMk, desc5, DialogModes.NO );
// =======================================================
var idset = stringIDToTypeID( "set" );
var desc50 = new ActionDescriptor();
var idnull = stringIDToTypeID( "null" );
var ref5 = new ActionReference();
ref5.putEnumerated( idadjustmentLayer, idordinal, idtargetEnum );
desc50.putReference( idnull, ref5 );
var idto = stringIDToTypeID( "to" );
var desc51 = new ActionDescriptor();
var list5 = new ActionList();
var desc52red = new ActionDescriptor();
var ref6 = new ActionReference();
ref6.putEnumerated( idchannel, idchannel, stringIDToTypeID( "red" ) );
desc52red.putReference( idchannel, ref6 );
var list6 = new ActionList();
var thisArray = theArray[0];
for (var m = 0; m < thisArray.length; m++) {
addCurvePoint (list6, thisArray[m])
};
desc52red.putList( idcurve, list6 );
var desc52green = new ActionDescriptor();
var ref6 = new ActionReference();
var thisArray = theArray[1];
ref6.putEnumerated( idchannel, idchannel, stringIDToTypeID( "green" ) );
desc52green.putReference( idchannel, ref6 );
var list6 = new ActionList();
for (var m = 0; m < thisArray.length; m++) {
addCurvePoint (list6, thisArray[m])
};
desc52green.putList( idcurve, list6 );
var desc52blue = new ActionDescriptor();
var ref6 = new ActionReference();
var thisArray = theArray[2];
ref6.putEnumerated( idchannel, idchannel, stringIDToTypeID( "blue" ) );
desc52blue.putReference( idchannel, ref6 );
var list6 = new ActionList();
for (var m = 0; m < thisArray.length; m++) {
addCurvePoint (list6, thisArray[m])
};
desc52blue.putList( idcurve, list6 );
list5.putObject( idcurvesAdjustment, desc52red );
list5.putObject( idcurvesAdjustment, desc52green );
list5.putObject( idcurvesAdjustment, desc52blue );
desc51.putList( idadjustment, list5 );
desc50.putObject( idto, idcurves, desc51 );
executeAction( idset, desc50, DialogModes.NO );
////////////////////////////////////
return app.activeDocument.activeLayer;
////// add curve point //////
function addCurvePoint (theList, valuePair) {
var desc11 = new ActionDescriptor();
desc11.putDouble( charIDToTypeID( "Hrzn" ), valuePair[0] );
desc11.putDouble( charIDToTypeID( "Vrtc" ), valuePair[1] );
theList.putObject( charIDToTypeID( "Pnt " ), desc11 );
};
};