• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Help optimising a script to make a visualisation of total ink coverage

Community Beginner ,
May 05, 2020 May 05, 2020

Copy link to clipboard

Copied

I have written a script where it creates a new layer from merged pixels and samples each pixel calclulates ink coverage and marks the pixel with one color if it is above the cutoff value and another if it is below. The only issue is it is really slow. I need help optimising this to improve performance. I am using photoshop CS6 JS. Below is the script

 

//samples each point in image and shows where the TAC is higher than normal (> 240)
//makes a new layer off all visible and performs the test

//CONSTANSTS

var C_ACCEPTABLE_TAC = 236
var C_ABOVE_TAC_COLOR = new SolidColor();
C_ABOVE_TAC_COLOR.rgb.hexValue = "f449ff"// bright magenta
var C_WITHIN_TAC_COLOR =  new SolidColor();
C_WITHIN_TAC_COLOR.rgb.hexValue = "ffffff"// white

//clear ES Toolkit console
if (app.name === "ExtendScript Toolkit") { 
  app.clc(); 
}
/*
// uncomment if you want ES to start even when photoshop launches the script
else {
  var estApp= BridgeTalk.getSpecifier("estoolkit");
  if(estApp) {
    var bt = new BridgeTalk;
    bt.target = estApp;
    bt.body = "app.clc()";
    bt.send();
  }
}
*/

var docRef = app.activeDocument;

function makeSelection(x,y,sw,sh){
    app.activeDocument.selection.select([ [x,y], [x,y+sh], [x+sw,y+sh], [x+sw,y] ]);  
}

//make a new layer empty layer
var newlayerRef = docRef.artLayers.add();
newlayerRef.kind = LayerKind.NORMAL;
newlayerRef.name = "IG TAC";

//set active layer to new layer
docRef.activeLayer = newlayerRef;

//copy merged (step afterwards in case only 1 layer)
docRef.selection.selectAll();
docRef.selection.copy(true) // copy merged;


//paste the selection into active layer
docRef.selection.selectAll();
docRef.paste();

//deselect selection
docRef.selection.deselect();

//process selection pixel by pixel to check TAC
app.preferences.rulerUnits = Units.PIXELS;
var docHeightPix = docRef.height
var docWidthPix = docRef.width

//$.writeln (docHeightPix)
//$.writeln (docWidthPix)

//$.writeln ("==")
for (var row=0; row <docHeightPix; row = row + 1) {
  if (row < (docHeightPix -1)) {
	  adjustedRow = row + 0.1
  }else {
	  adjustedRow = row
  }
  
  for (var col=0; col <docWidthPix; col = col + 1) {
	//var adjustedCol = (row == app.activeDocument.width -1) ? col : col + 0.1;
	if (col < (docWidthPix -1)) {
	  adjustedCol = col + 0.1
	} else {
	  adjustedCol = col;
	}
	 //$.writeln("[" + row + ", " + col + "]");
	 //$.writeln("[" + adjustedRow + ", " + adjustedCol + "]");
	 docRef.colorSamplers.removeAll();
	 docRef.colorSamplers.add([adjustedCol, adjustedRow]);//workaround for reliable pixel color picking.
	 var cs = docRef.colorSamplers[0];
	 var totalInk = cs.color.cmyk.cyan + cs.color.cmyk.magenta  + cs.color.cmyk.yellow  + cs.color.cmyk.black;
	 totalInk = Math.round(totalInk);
	 //$.writeln(totalInk);
	 // color the pixels
	makeSelection(col,row,1,1)
	 if (totalInk > C_ACCEPTABLE_TAC){
		 docRef.selection.fill(C_ABOVE_TAC_COLOR)
	 } else {
		 docRef.selection.fill(C_WITHIN_TAC_COLOR)
	 }
	 
  }
  //$.writeln("#^");
}

app.activeDocument.colorSamplers.removeAll();

.Any help would be appreaciated.

TOPICS
Actions and scripting

Views

3.1K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 4 Correct answers

Community Expert , May 05, 2020 May 05, 2020

I have not tested your script, however, you may be interested in looking at this action;

 

https://curvemeister.com/downloads/cmyk_tac/index.htm

Votes

Translate

Translate
Community Beginner , May 05, 2020 May 05, 2020

Thank you so much. It seems to work really well.. The roundtrip to acrobat and back is very time consuming.

Votes

Translate

Translate
Community Expert , May 06, 2020 May 06, 2020

Here is a quick script conversion from the action. I have used a prompt to enter the total ink limit value. I am still trying to get the variable inkCalc to convert to an integer if the prompt entry was not a whole number. Please feel free to clue me in as I have not had any success with various math methods such as parseInt etc.

 

 

// cmyk_total_ink_limits.jsx

/*
Based on the cmyk_total_ink_limits.atn from Curvemeister.com
https://curvemeister.com/downloads/cmyk_tac/index.htm

Original Curvem
...

Votes

Translate

Translate
Community Expert , May 06, 2020 May 06, 2020

Votes

Translate

Translate
Adobe
Community Expert ,
May 05, 2020 May 05, 2020

Copy link to clipboard

Copied

I have not tested your script, however, you may be interested in looking at this action;

 

https://curvemeister.com/downloads/cmyk_tac/index.htm

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 05, 2020 May 05, 2020

Copy link to clipboard

Copied

Thank you so much. It seems to work really well.. The roundtrip to acrobat and back is very time consuming.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 05, 2020 May 05, 2020

Copy link to clipboard

Copied

You are processing each pixels in a document one at time in a script that need to have its code interpret for each pixel process. an image 1,000px x 1,000px at 300PPI would be 3.3" by 3.3" printed image that contains 1,000,000 pixels.  How long  and hard would  you have to work to make 1$ for each pixel.  You can not expect a script to do a million calculations in a short amount of time. The would required a compiled program optimized  for your particular calculation.  You  should look for compiled plug-in that does what you want. If you want to process a Camera digit image.  Phones these days capture 12,000,000 pixels for an image.  A 4K TV image is 8MP, a 8K TV image is 33MP.  How long would it take you to twiddle your thumbs 1,000,000 rotations.

JJMack

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 05, 2020 May 05, 2020

Copy link to clipboard

Copied

Yes makes sense. I was having a look at numpy and python to do the calculations faster. But thanks to Stephen on top that photoshop action works perfectly.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 06, 2020 May 06, 2020

Copy link to clipboard

Copied

Here is a quick script conversion from the action. I have used a prompt to enter the total ink limit value. I am still trying to get the variable inkCalc to convert to an integer if the prompt entry was not a whole number. Please feel free to clue me in as I have not had any success with various math methods such as parseInt etc.

 

 

// cmyk_total_ink_limits.jsx

/*
Based on the cmyk_total_ink_limits.atn from Curvemeister.com
https://curvemeister.com/downloads/cmyk_tac/index.htm

Original Curvemeister.com action converted to script using xtools
https://sourceforge.net/projects/ps-scripts/files/xtools/
https://www.ps-scripts.com/viewforum.php?f=53

Help optimizing a script to make a visualization of total ink coverage
https://community.adobe.com/t5/photoshop/help-optimising-a-script-to-make-a-visualisation-of-total-ink-coverage/td-p/11107667
*/

#target photoshop
app.bringToFront();

(function () {

    cTID = function (s) { return app.charIDToTypeID(s); };
    sTID = function (s) { return app.stringIDToTypeID(s); };

    function CMYKTIL() {
        // Duplicate
        function step1(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putEnumerated(cTID('Dcmn'), cTID('Ordn'), cTID('Frst'));
            desc1.putReference(cTID('null'), ref1);
            executeAction(cTID('Dplc'), desc1, dialogMode);
        };

        // Flatten Image
        function step2(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            executeAction(sTID('flattenImage'), undefined, dialogMode);
        };

        // Convert Mode
        function step3(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putInteger(cTID('Dpth'), 16);
            executeAction(sTID('convertMode'), desc1, dialogMode);
        };

        // Invert
        function step4(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            executeAction(cTID('Invr'), undefined, dialogMode);
        };

        // Curves
        function step5(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            var list1 = new ActionList();
            var desc2 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Cmps'));
            desc2.putReference(cTID('Chnl'), ref1);
            var list2 = new ActionList();
            var desc3 = new ActionDescriptor();
            desc3.putDouble(cTID('Hrzn'), 0);
            desc3.putDouble(cTID('Vrtc'), 0);
            list2.putObject(cTID('Pnt '), desc3);
            var desc4 = new ActionDescriptor();
            desc4.putDouble(cTID('Hrzn'), 255);
            desc4.putDouble(cTID('Vrtc'), 64);
            list2.putObject(cTID('Pnt '), desc4);
            desc2.putList(cTID('Crv '), list2);
            list1.putObject(cTID('CrvA'), desc2);
            desc1.putList(cTID('Adjs'), list1);
            executeAction(cTID('Crvs'), desc1, dialogMode);
        };

        // Make
        function step6(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putClass(cTID('Nw  '), cTID('Chnl'));
            var desc2 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Cyn '));
            desc2.putReference(cTID('T   '), ref1);
            desc2.putEnumerated(cTID('Clcl'), cTID('Clcn'), cTID('Add '));
            desc2.putDouble(cTID('Scl '), 1.00000004749745);
            desc2.putInteger(cTID('Ofst'), 0);
            var ref2 = new ActionReference();
            ref2.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Mgnt'));
            desc2.putReference(cTID('Src2'), ref2);
            desc1.putObject(cTID('Usng'), cTID('Clcl'), desc2);
            executeAction(cTID('Mk  '), desc1, dialogMode);
        };

        // Make
        function step7(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putClass(cTID('Nw  '), cTID('Chnl'));
            var desc2 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putEnumerated(cTID('Chnl'), cTID('Ordn'), cTID('Trgt'));
            desc2.putReference(cTID('T   '), ref1);
            desc2.putEnumerated(cTID('Clcl'), cTID('Clcn'), cTID('Add '));
            desc2.putDouble(cTID('Scl '), 1.00000004749745);
            desc2.putInteger(cTID('Ofst'), 0);
            var ref2 = new ActionReference();
            ref2.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Yllw'));
            desc2.putReference(cTID('Src2'), ref2);
            desc1.putObject(cTID('Usng'), cTID('Clcl'), desc2);
            executeAction(cTID('Mk  '), desc1, dialogMode);
        };

        // Make
        function step8(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putClass(cTID('Nw  '), cTID('Chnl'));
            var desc2 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Blck'));
            desc2.putReference(cTID('T   '), ref1);
            desc2.putEnumerated(cTID('Clcl'), cTID('Clcn'), cTID('Add '));
            desc2.putDouble(cTID('Scl '), 1.00000004749745);
            desc2.putInteger(cTID('Ofst'), 0);
            var ref2 = new ActionReference();
            ref2.putEnumerated(cTID('Chnl'), cTID('Ordn'), cTID('Trgt'));
            desc2.putReference(cTID('Src2'), ref2);
            desc1.putObject(cTID('Usng'), cTID('Clcl'), desc2);
            executeAction(cTID('Mk  '), desc1, dialogMode);
        };

        /* 
        https://curvemeister.com/downloads/cmyk_tac/index.htm
        To calculate the threshold value, divide the total ink limit by 400, and multiply by 255.
        For example, a TIL value of 300 would require a threshold value of 255 * 300/400 = 191
        */

        var inkLimitIn = prompt('Enter the total ink limit as a whole number/integer (i.e. 300):', '');
        // Test if cancel returns null, then do nothing.
        if (inkLimitIn == null) {
            alert('Script cancelled!');
            return
        };
        // Test if an empty string is returned, then do nothing.
        if (inkLimitIn == "") {
            alert('A value was not entered, script cancelled!');
            return
        };

        /* https://prepression.blogspot.com/2019/03/bridge-batch-rename-to-clean-invalid.html */
        var inkLimitOut = inkLimitIn.replace(/[^0-9]/gi, '');
        var inkCalc = 255 * inkLimitOut / 400;

        // Threshold
        function step9(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putInteger(cTID('Lvl '), inkCalc); // Variable
            executeAction(cTID('Thrs'), desc1, dialogMode);
        };

        // Set
        function step10(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putProperty(cTID('Chnl'), sTID("selection"));
            desc1.putReference(cTID('null'), ref1);
            desc1.putEnumerated(cTID('T   '), cTID('Ordn'), cTID('Al  '));
            executeAction(cTID('setd'), desc1, dialogMode);
        };

        // Copy
        function step11(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            executeAction(cTID('copy'), undefined, dialogMode);
        };

        // Close
        function step12(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putEnumerated(cTID('Svng'), cTID('YsN '), cTID('N   '));
            executeAction(cTID('Cls '), desc1, dialogMode);
        };

        // Set
        function step13(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putProperty(cTID('Prpr'), cTID('QucM'));
            ref1.putEnumerated(cTID('Dcmn'), cTID('Ordn'), cTID('Trgt'));
            desc1.putReference(cTID('null'), ref1);
            executeAction(cTID('setd'), desc1, dialogMode);
        };

        // Paste
        function step14(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            desc1.putEnumerated(cTID('AntA'), cTID('Annt'), cTID('Anno'));
            executeAction(cTID('past'), desc1, dialogMode);
        };

        // Clear
        function step15(enabled, withDialog) {
            if (enabled != undefined && !enabled)
                return;
            var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
            var desc1 = new ActionDescriptor();
            var ref1 = new ActionReference();
            ref1.putProperty(cTID('Prpr'), cTID('QucM'));
            ref1.putEnumerated(cTID('Dcmn'), cTID('Ordn'), cTID('Trgt'));
            desc1.putReference(cTID('null'), ref1);
            executeAction(cTID('Cler'), desc1, dialogMode);
        };

        step1();      // Duplicate
        step2();      // Flatten Image
        step3();      // Convert Mode
        step4();      // Invert
        step5();      // Curves
        step6();      // Make
        step7();      // Make
        step8();      // Make
        step9();      // Threshold
        step10();      // Set
        step11();      // Copy
        step12();      // Close
        step13();      // Set
        step14();      // Paste
        step15();      // Clear
    };

    CMYKTIL.main = function () {
        CMYKTIL();
    };

    CMYKTIL.main();

    "CMYKTIL.jsx"

})();

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 06, 2020 May 06, 2020

Copy link to clipboard

Copied

This looks interesting. One of the issues I had faced while testing out the action was that the threshold action would only accept int values. This led to more imprecise selection. I am very interested in how you converted the action into a script how could I do that on Windows?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 06, 2020 May 06, 2020

Copy link to clipboard

Copied

Just make sure that the entry into the GUI prompt is a whole number, I couldn't get the math working to clean the input correctly if one entered a float with one or more digits.

 

The original action would return a rounded down integer of 191 for a 300% total ink limit (255 * 300 / 400).

 

The script returns 191.25 against the variable (255 * inkLimitOut / 400).

 

I documented how the script was created from the action in the comments at the head, using xbytor's xtools.

 

https://sourceforge.net/projects/ps-scripts/files/xtools/

https://www.ps-scripts.com/viewforum.php?f=53

 

I would usually write the script from scratch, however, in this case, I just wanted to quickly see how workable it would be to replace the hardcoded action steps with a variable. Rather than using a prompt to enter the ink limit, a simple GUI could instead offer a dropdown-list of predefined values.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 06, 2020 May 06, 2020

Copy link to clipboard

Copied

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 06, 2020 May 06, 2020

Copy link to clipboard

Copied

LATEST

Nice!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
May 06, 2020 May 06, 2020

Copy link to clipboard

Copied

I can offer to save the file as a Photoshop RAW file. Read this file in binary form to a string. It will be an analogue of an array of pixel values (RGB or CMYK).
Read this string character by character. Analyze values (as RGB or CMYK values). Already correct values are added to the new string character by character. Save the new string back to the file. Open the file and take a layer from it as a result.
 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines