0

I'm often having to align and crop product images on a white back drop and it occurred to me this would be a simple task for a script of some sort.

I can think of a way to code this as an after effects script (as thats what im used to), but wondering if anything like this already exists, perhaps as photoshop script?

Searching brings up a lot of stuff aimed at HDR alignment etc. But what I need is something that would either scale or crop an image by e.g searching the margins for the first significant pixels (set by threshold) and cropping to there. Or more ideally in my case, aligning those margin positions to guides.

EDIT (added screenshots as requested)

Say for example I have 100s of images like this all off centre with varying amounts of excess space on the sides. enter image description here

Right now I'm manually aligning them using guides, but it seems to me this would be easily achievable by a script or plugin to search the edges for pixels within a range of brightness and call that the edge of the 'bounding box'. This bounding box could then be scaled/centered within some static dimensions. I could script this in After Effects using each image as a seperate frame. But I'd rather not reinvent the wheel if something similar already exists.

enter image description here

  • It'd be helpful if you show some screenshots of what you're trying to achieve. – Sergey Kritskiy Aug 27 '18 at 22:47
  • If the contrast between the object and the background is consistent enough, it might be possible to use Curves or Levels to blow out the background so it's one solid color and then Image > Trim. The issue with this method is that if the contrast between the object and background isn't strong enough, you might end up trimming too much or too little. – Joonas Aug 28 '18 at 6:56
2

Don't know if there's an existing script, but here's how I'd do that. This script will ask for Levels input to define what areas to crop and then crop it. If your scenes are mostly contrast, you can set askForLevelsInput to false, it will use default values then.

Results, first with asking for input, second is an automatic. In this case automatic worked perfectly.

enter image description here

enter image description here

function cropByBoundingBox()
{

    //variables
    var askForLevelsInput = true,
        expandValue = 20,
        leftLevelHandle = 55,
        rightLevelHandle = 200;

    //creating a preview/mask generator layer
    try
    {
        //if there're several layers, merge them together and send it to top of the layer stack
        var mergeLayer = mergeVisible();
        sendUp();
    }
    catch (e)
    {
        //if there's only one background layer, duplicate it
        var mergeLayer = duplicate();
    }

    //desaturate merged layer
    desaturate();

    //asking for levels input
    if (!levels(
        {
            left: leftLevelHandle,
            right: rightLevelHandle,
            ask: askForLevelsInput,
        }))
    {
        //if levels were cancelled, aborting
        mergeLayer.remove();
        return false
    }

    //load selection based on red channel
    selectionByRedChannel();

    //inverting selection so that the object will be selected
    invertSelection();

    //expanding selection to get some space
    expandSelection(expandValue);

    //cropping to that selection
    cropToSelection();

    //removing selection
    deselect();

    //removing the preview layer
    mergeLayer.remove();

    /////////////////////////////////////////////////////////////////////////////////////
    // FUNCTIONS
    /////////////////////////////////////////////////////////////////////////////////////

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

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

    function mergeVisible()
    {
        var desc5 = new ActionDescriptor();
        desc5.putBoolean(cTID('Dplc'), _keepThings);
        executeAction(cTID('MrgV'), desc5, DialogModes.NO);
        return activeDocument.activeLayer
    };

    function sendUp()
    {
        var desc3 = new ActionDescriptor();
        var ref1 = new ActionReference();
        ref1.putEnumerated(cTID('Lyr '), cTID('Ordn'), cTID('Trgt'));
        desc3.putReference(cTID('null'), ref1);
        var ref2 = new ActionReference();
        ref2.putEnumerated(cTID('Lyr '), cTID('Ordn'), cTID('Frnt'));
        desc3.putReference(cTID('T   '), ref2);
        executeAction(cTID('move'), desc3, DialogModes.NO);
    };

    function duplicate()
    {
        executeAction(cTID('CpTL'), undefined, DialogModes.NO);
        return activeDocument.activeLayer
    };

    function desaturate()
    {
        executeAction(cTID('Dstt'), undefined, DialogModes.NO);
    };

    function levels(_values)
    {
        _values == undefined && _values = {};
        _values.left == undefined && _values.left = 0;
        _values.right == undefined && _values.right = 255;
        _values.middle == undefined && _values.middle = 1;
        _values.ask == undefined && _values.ask = false;

        var desc36 = new ActionDescriptor();
        desc36.putEnumerated(sTID('presetKind'), sTID('presetKindType'), sTID('presetKindCustom'));
        var list1 = new ActionList();
        var desc37 = new ActionDescriptor();
        var ref5 = new ActionReference();
        ref5.putEnumerated(cTID('Chnl'), cTID('Ordn'), cTID('Trgt'));
        desc37.putReference(cTID('Chnl'), ref5);
        var list2 = new ActionList();
        list2.putInteger(_values.left);
        list2.putInteger(_values.right);
        desc37.putList(cTID('Inpt'), list2);
        desc37.putDouble(cTID('Gmm '), _values.middle);
        list1.putObject(cTID('LvlA'), desc37);
        desc36.putList(cTID('Adjs'), list1);

        try
        {
            executeAction(cTID('Lvls'), desc36, _values.ask ? DialogModes.ALL : DialogModes.NO);
            return true
        }
        catch (e)
        {
            return false
        }
    };

    function selectionByRedChannel()
    {
        var desc8833 = new ActionDescriptor();
        var ref911 = new ActionReference();
        ref911.putProperty(cTID('Chnl'), cTID('fsel'));
        desc8833.putReference(cTID('null'), ref911);
        var ref912 = new ActionReference();
        ref912.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Rd  '));
        desc8833.putReference(cTID('T   '), ref912);
        executeAction(cTID('setd'), desc8833, DialogModes.NO);
    };

    function invertSelection()
    {
        executeAction(cTID('Invs'), undefined, DialogModes.NO);
    };

    function expandSelection(_value)
    {
        var desc62 = new ActionDescriptor();
        desc62.putUnitDouble(cTID('By  '), cTID('#Pxl'), _value);
        desc62.putBoolean(sTID('selectionModifyEffectAtCanvasBounds'), true);
        executeAction(cTID('Expn'), desc62, DialogModes.NO);
    };

    function cropToSelection()
    {
        var desc33 = new ActionDescriptor();
        desc33.putBoolean(cTID('Dlt '), true);
        executeAction(cTID('Crop'), desc33, DialogModes.NO);
    };

    function deselect()
    {
        var desc5 = new ActionDescriptor();
        var ref1 = new ActionReference();
        ref1.putProperty(cTID('Chnl'), cTID('fsel'));
        desc5.putReference(cTID('null'), ref1);
        desc5.putEnumerated(cTID('T   '), cTID('Ordn'), cTID('None'));
        executeAction(cTID('setd'), desc5, DialogModes.NO);
    };

}
app.activeDocument.suspendHistory("cropByBoundingBox", "cropByBoundingBox()");
  • THe levels could be automated aswell by hardcoding it down if need be – joojaa Aug 28 '18 at 10:14
  • yeah, they are hardcoded (55 for the left handler and 200 for the right one), I just didn't bring the values to the top, to all the variables definitions. I'll do that now. – Sergey Kritskiy Aug 28 '18 at 10:34
  • 1
    you might bring those as input values to main and call main something more descriptive. – joojaa Aug 28 '18 at 10:36
1

If you have the most recent version of Photoshop CC which has a new automated Select Subject feature, you could automate by recording an Action, without the need to write a script.

Include the following steps in an Action:

  1. Select > Subject
  2. Select > Modify > Expand, enter number of pixels (in example below I used 20 pixels)
  3. Image > Crop

enter image description here

And finally, to automate it, apply the recorded Action to a whole folder full of images using File > Automate > Batch

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.