Skip to main content
T.S..
Participant
November 22, 2023
Question

Detecting white/coloured pixels in a layer (and crop it without deleting the pixels) via script?

  • November 22, 2023
  • 2 replies
  • 918 views

Hi everyone! I'm trying my best to write a javascript that checks the bounding box of a black and white matte (in therory it could also have different colours against black). After that it should crop the bounding box but still keep the pixels in the background.

 

I've found the croping code (https://stackoverflow.com/questions/64246854/crop-without-deleting-cropped-pixels-photoshop-extendscript), but for the colour selection I'm out of luck.

 

modified croping code:

crop({
  left: matte-l,
  top: matte-t,
  right: matte-r,
  bottom: matte-b,
  deleteCropped: false
});

function crop(data)
{
  if (data.deleteCropped == undefined) data.deleteCropped = true; // default value

  var desc = new ActionDescriptor();
  var descRectangle = new ActionDescriptor();
  descRectangle.putUnitDouble(charIDToTypeID('Top '), charIDToTypeID('#Pxl'), data.top);
  descRectangle.putUnitDouble(charIDToTypeID('Left'), charIDToTypeID('#Pxl'), data.left);
  descRectangle.putUnitDouble(charIDToTypeID('Btom'), charIDToTypeID('#Pxl'), data.bottom);
  descRectangle.putUnitDouble(charIDToTypeID('Rght'), charIDToTypeID('#Pxl'), data.right);
  desc.putObject(charIDToTypeID('T   '), charIDToTypeID('Rctn'), descRectangle);
  desc.putUnitDouble( charIDToTypeID('Angl'), charIDToTypeID('#Ang'), 0.000000 );
  desc.putBoolean(charIDToTypeID('Dlt '), data.deleteCropped);
  executeAction(charIDToTypeID('Crop'), desc, DialogModes.NO);
} // end of crop()

 

Any ideas on how to achive this?

 

best regards,

Till

This topic has been closed for replies.

2 replies

Legend
November 22, 2023

try this:

 

#target photoshop
const s2t = stringIDToTypeID;
var activeHistoryState = activeDocument.activeHistoryState;

// convert document to RGB
(d = new ActionDescriptor()).putClass(s2t('to'), s2t('RGBColorMode'));
executeAction(s2t('convertMode'), d, DialogModes.NO);

var RGB = [];

// calculating background fill color (based on histogram)
for (var i = 1; i <= 3; i++) {
    (r = new ActionReference()).putProperty(s2t('property'), p = s2t('histogram'));
    r.putIndex(s2t('channel'), i);
    var histogram = executeActionGet(r).getList(p),
        max = 0, value = 0;
    for (var n = 0; n < histogram.count; n++) {
        var cur = histogram.getInteger(n);
        if (cur > max) {
            max = cur;
            value = n;
        }
    }
    RGB.push(value);
}

// fill document with found color in difference mode
(d = new ActionDescriptor()).putEnumerated(s2t('using'), s2t('fillContents'), s2t('color'));
(d1 = new ActionDescriptor()).putDouble(s2t('red'), RGB[0]);
d1.putDouble(s2t('green'), RGB[1]);
d1.putDouble(s2t('blue'), RGB[2]);
d.putObject(s2t('color'), s2t('RGBColor'), d1);
d.putUnitDouble(s2t('opacity'), s2t('percentUnit'), 100);
d.putEnumerated(s2t('mode'), s2t('blendMode'), s2t('difference'));
executeAction(s2t('fill'), d, DialogModes.NO);

// make selection to RGB channel
(r = new ActionReference()).putProperty(s2t('channel'), s2t('selection'));
(d = new ActionDescriptor()).putReference(s2t('target'), r);
(r1 = new ActionReference()).putEnumerated(s2t('channel'), s2t('channel'), s2t('RGB'));
d.putReference(s2t('to'), r1);
executeAction(s2t('set'), d, DialogModes.NO);

//save selection to variable
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('selection'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var selection = executeActionGet(r).getObjectValue(p);

// return initial state of document
activeDocument.activeHistoryState = activeHistoryState;

// crop to saved selection
(d = new ActionDescriptor()).putObject(s2t('to'), s2t('rectangle'), selection);
d.putUnitDouble(s2t('angle'), s2t('angle'), 0);
d.putBoolean(s2t('delete'), false);
executeAction(s2t('crop'), d, DialogModes.NO);

 

 

We can also simplify the code and use the select subject function, but the accuracy of determining the boundaries of the object may vary:

 

#target photoshop
const s2t = stringIDToTypeID;
var activeHistoryState = activeDocument.activeHistoryState;

// convert document to RGB
(d = new ActionDescriptor()).putClass(s2t('to'), s2t('RGBColorMode'));
executeAction(s2t('convertMode'), d, DialogModes.NO);

//call select subject function
(d = new ActionDescriptor()).putBoolean(s2t('sampleAllLayers'), true);
executeAction(s2t('autoCutout'), d, DialogModes.NO);

//save selection to variable
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('selection'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var selection = executeActionGet(r).getObjectValue(p);

// return initial state of document
activeDocument.activeHistoryState = activeHistoryState;

// crop to saved selection
(d = new ActionDescriptor()).putObject(s2t('to'), s2t('rectangle'), selection);
d.putUnitDouble(s2t('angle'), s2t('angle'), 0);
d.putBoolean(s2t('delete'), false);
executeAction(s2t('crop'), d, DialogModes.NO);

 

Stephen Marsh
Community Expert
Community Expert
November 22, 2023

A before and after image sample is always helpful.

T.S..
T.S..Author
Participant
November 22, 2023

Of course... here is a sample of the layer.

The other layers contain different information, so just selecting the color of the document doesn't work.

Legend
November 23, 2023
Simple option.
Convert the layer to a smart object (if you need to save the cut pixels).
After that do Menu->Image->Trim (based on top-left pixel color)
 
 
 

Ingenious!