Skip to main content
Known Participant
July 15, 2023
Answered

How to using script crop image by aspect ratio ?

  • July 15, 2023
  • 2 replies
  • 1832 views

Thanks to @c.pfaffenbichler teach me how to detact best fit ration for various images, however, photoshop scripts looks do no support crop with ratio

app.activeDocument.crop

from Abobe official guide - crop (bounds, [, angle], [, width], [, height])

 

means it only support crop in pixels dimention (not accurate), any methods to crop in ratio same as we use crop tools ?

This topic has been closed for replies.
Correct answer Stephen Marsh

@Calvin21914323wi10 – Try this:

 

/*
Auto Crop to Nearest Aspect Ratio.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-using-script-crop-image-by-aspect-ratio/td-p/13938028
From:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/is-possible-to-write-a-script-that-can-auto-crop-image-to-best-fit-aspect-ratio/td-p/13883327
By Calvin21914323wi10 
Based on code from c.pfaffenbichler
#2 - Amendments by Stephen Marsh
*/

#target photoshop

if (app.documents.length > 0) {

    app.togglePalettes();
    var originalRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PIXELS;
    var myDocument = activeDocument;
    var theW = myDocument.width.value;
    var theH = myDocument.height.value;
    var theProportion = theW / theH;
    var theProportions = [[1,3],[1,2],[2,3], [5,7], [3,4], [4,5], [1,1], [5,4], [4,3], [7,5], [3,2], [2,1], [3,1]];

    var theResult = theProportions.sort(sortArrayByDifference)[0];
    var thisProp = theResult[0] / theResult[1];

    if (theProportion < thisProp) {
        var targetW = theW;
        var targetH = theW * theResult[1] / theResult[0];
    } else {
        var targetW = theH * theResult[0] / theResult[1];
        var targetH = theH;
    }

    // Calculate centre crop
    var diffW = theW - targetW;
    var offsetW = diffW / 2;
    var diffH = theH - targetH;
    var offsetH = diffH / 2;
    var bounds = [offsetW, offsetH, Math.round(targetW) + offsetW, Math.round(targetH) + offsetH];
    //var bounds = [0, 0, targetW, targetH];

    // Disable or remove the alert for batch processing
    alert("At the aspect ratio " + theResult.join(":") + " the image would be" + "\n" + targetW + " x " + targetH + " px");

    /* https://theiviaxx.github.io/photoshop-docs/Photoshop/Document/crop.html */
    // Crop to bounds
    app.activeDocument.crop(bounds,null,null,null);

    app.preferences.rulerUnits = originalRulerUnits;
    app.togglePalettes();
}

////// sort by difference to a number //////
function sortArrayByDifference(a, b) {
    if (Math.abs((a[0] / a[1]) - theProportion) < Math.abs((b[0] / b[1]) - theProportion)) return -1;
    if (Math.abs((a[0] / a[1]) - theProportion) > Math.abs((b[0] / b[1]) - theProportion)) return 1;
    return 0;
}

 

P.S. I have revised the original code a little, make sure that you are using the updated version #2.

 

2 replies

Stephen Marsh
Community Expert
Community Expert
July 15, 2023
quote

means it only support crop in pixels dimention (not accurate), any methods to crop in ratio same as we use crop tools ?


By @Calvin21914323wi10


Images are composed of pixels, Photoshop works in pixels, so other units are therefore derived from pixels. Why do you say that cropping by pixel dimension isn't accurate if the end result is the correct aspect ratio?

 

Take a 1100 x 1000 pixel sized image as an example, using the crop tool to crop to 1:1 square ratio for a centre crop. The ScriptListener recorded AM code looks like this:

 

var idcrop = stringIDToTypeID( "crop" );
    var desc298 = new ActionDescriptor();
    var idto = stringIDToTypeID( "to" );
        var desc299 = new ActionDescriptor();
        var idtop = stringIDToTypeID( "top" );
        var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
        desc299.putUnitDouble( idtop, idpixelsUnit, 0.000000 );
        var idleft = stringIDToTypeID( "left" );
        var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
        desc299.putUnitDouble( idleft, idpixelsUnit, 50.000000 );
        var idbottom = stringIDToTypeID( "bottom" );
        var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
        desc299.putUnitDouble( idbottom, idpixelsUnit, 1000.000000 );
        var idright = stringIDToTypeID( "right" );
        var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
        desc299.putUnitDouble( idright, idpixelsUnit, 1050.000000 );
    var idrectangle = stringIDToTypeID( "rectangle" );
    desc298.putObject( idto, idrectangle, desc299 );
    var idangle = stringIDToTypeID( "angle" );
    var idangleUnit = stringIDToTypeID( "angleUnit" );
    desc298.putUnitDouble( idangle, idangleUnit, 0.000000 );
    var iddelete = stringIDToTypeID( "delete" );
    desc298.putBoolean( iddelete, true );
    var idcropAspectRatioModeKey = stringIDToTypeID( "cropAspectRatioModeKey" );
    var idcropAspectRatioModeClass = stringIDToTypeID( "cropAspectRatioModeClass" );
    var idpureAspectRatio = stringIDToTypeID( "pureAspectRatio" );
    desc298.putEnumerated( idcropAspectRatioModeKey, idcropAspectRatioModeClass, idpureAspectRatio );
    var idconstrainProportions = stringIDToTypeID( "constrainProportions" );
    desc298.putBoolean( idconstrainProportions, true );
executeAction( idcrop, desc298, DialogModes.NO );

 

Here it is cleaned into a function, which might be easier to read:

 

crop(0, 50, 1000, 1050, 0, true, true);

function crop(top, left, bottom, right, angle, delete2, constrainProportions) {
	var s2t = function (s) {
		return app.stringIDToTypeID(s);
	};

	var descriptor = new ActionDescriptor();
	var descriptor2 = new ActionDescriptor();

	descriptor2.putUnitDouble( s2t( "top" ), s2t( "pixelsUnit" ), top );
	descriptor2.putUnitDouble( s2t( "left" ), s2t( "pixelsUnit" ), left );
	descriptor2.putUnitDouble( s2t( "bottom" ), s2t( "pixelsUnit" ), bottom );
	descriptor2.putUnitDouble( s2t( "right" ), s2t( "pixelsUnit" ), right );
	descriptor.putObject( s2t( "to" ), s2t( "rectangle" ), descriptor2 );
	descriptor.putUnitDouble( s2t( "angle" ), s2t( "angleUnit" ), angle );
	descriptor.putBoolean( s2t( "delete" ), delete2 );
	descriptor.putEnumerated( s2t( "cropAspectRatioModeKey" ), s2t( "cropAspectRatioModeClass" ), s2t( "pureAspectRatio" ));
	descriptor.putBoolean( s2t( "constrainProportions" ), constrainProportions );
	executeAction( s2t( "crop" ), descriptor, DialogModes.NO );
}

 

So even the crop tool works in pixels when you select a ratio.

 

Anther way to automate cropping is to crop from a selection using the Image > Crop command:

 

// A selection must be active
executeAction(stringIDToTypeID("crop"), undefined, DialogModes.NO);
// You may wish to deselect

 

So you can certainly create a temporary layer object at a specific ratio and position, load a selection, crop to the selection and remove the temporary object.

 

Finally, you can use canvas size to crop.

 

Perhaps the following links will help:

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/script-help-crop-based-on-select-subject/m-p/10405382

 

https://community.adobe.com/t5/photoshop-ecosystem-discussions/find-crop-aspect-ratio-from-selection-or-image-solved/td-p/12981892

 

Known Participant
July 15, 2023

I am still a bit lost, here is an example, attached 3 file original, crop_tool, script_crop

 

Original is 800*1214, best fit ration is 2:3 which should be 800*1200

Crop Tool crop it 800*1200 - exact value

script_crop crop it to 800*1193 - ? ?

 

your calcuation is correct but my added code have problem - I think the mistake is in following

 

 

 

var bounds = [0,0,targetW,targetH]
app.activeDocument.crop(bounds);

 

 

I don't know how to set bounds - I think

How to crop in the centre of image ? any hints ?

 

 

 

Stephen Marsh
Community Expert
Stephen MarshCommunity ExpertCorrect answer
Community Expert
July 15, 2023

@Calvin21914323wi10 – Try this:

 

/*
Auto Crop to Nearest Aspect Ratio.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-using-script-crop-image-by-aspect-ratio/td-p/13938028
From:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/is-possible-to-write-a-script-that-can-auto-crop-image-to-best-fit-aspect-ratio/td-p/13883327
By Calvin21914323wi10 
Based on code from c.pfaffenbichler
#2 - Amendments by Stephen Marsh
*/

#target photoshop

if (app.documents.length > 0) {

    app.togglePalettes();
    var originalRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PIXELS;
    var myDocument = activeDocument;
    var theW = myDocument.width.value;
    var theH = myDocument.height.value;
    var theProportion = theW / theH;
    var theProportions = [[1,3],[1,2],[2,3], [5,7], [3,4], [4,5], [1,1], [5,4], [4,3], [7,5], [3,2], [2,1], [3,1]];

    var theResult = theProportions.sort(sortArrayByDifference)[0];
    var thisProp = theResult[0] / theResult[1];

    if (theProportion < thisProp) {
        var targetW = theW;
        var targetH = theW * theResult[1] / theResult[0];
    } else {
        var targetW = theH * theResult[0] / theResult[1];
        var targetH = theH;
    }

    // Calculate centre crop
    var diffW = theW - targetW;
    var offsetW = diffW / 2;
    var diffH = theH - targetH;
    var offsetH = diffH / 2;
    var bounds = [offsetW, offsetH, Math.round(targetW) + offsetW, Math.round(targetH) + offsetH];
    //var bounds = [0, 0, targetW, targetH];

    // Disable or remove the alert for batch processing
    alert("At the aspect ratio " + theResult.join(":") + " the image would be" + "\n" + targetW + " x " + targetH + " px");

    /* https://theiviaxx.github.io/photoshop-docs/Photoshop/Document/crop.html */
    // Crop to bounds
    app.activeDocument.crop(bounds,null,null,null);

    app.preferences.rulerUnits = originalRulerUnits;
    app.togglePalettes();
}

////// sort by difference to a number //////
function sortArrayByDifference(a, b) {
    if (Math.abs((a[0] / a[1]) - theProportion) < Math.abs((b[0] / b[1]) - theProportion)) return -1;
    if (Math.abs((a[0] / a[1]) - theProportion) > Math.abs((b[0] / b[1]) - theProportion)) return 1;
    return 0;
}

 

P.S. I have revised the original code a little, make sure that you are using the updated version #2.

 

Stephen Marsh
Community Expert
Community Expert
July 15, 2023