Skip to main content
Inspiring
June 11, 2024
Answered

I want to make a form to define crops.

  • June 11, 2024
  • 2 replies
  • 1494 views

I'm using Windows 10 with the latest build of Photoshop 2024.

I primarily take photos of birds.  Instead of playing around with the crop tool, can I have a form allow me to define the percentage of the top, bottom, left, and right to crop.  I’d then like to be able to click on a button that will define the subject and either perform the crop or outline it for me to review before proceeding. 

There are going to be times when the bird is so surrounded by vegetation that Photoshop will not be able to correctly define the subject, so in those cases, I would need to be able to use a tool to draw around the bird to define it.

Also, with a fixed percentage definition, can I then batch files with the same crop?

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

 

@Mark37430984r9lw 

 

You can try the following script. It's interactive, once you know what the average/common % margin values are, it is easy to update these values and turn off the interactive step so that this can be batched.

 

EDIT: I have updated the original interactive guide layout code to now work in % without manually setting the units.

 

The script will then crop to the guides and remove the guides. The cropped pixels can be retained or permanently deleted by changing false to true in the crop function call.

 

/*
Canvas Margin Percentage Crop.jsx
v1.2 - 12th June 2024, Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/i-want-to-make-a-form-to-define-crops/td-p/14675929
*/

#target photoshop

// Check if a document is open
if (app.documents.length > 0) {

    // Set the document variable
    var doc = app.activeDocument;

    // Get and set the ruler units
    var origRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PERCENT;

    // Conditionally clear existing guides
    if (app.activeDocument.guides.length > 0)
    app.runMenuItem(stringIDToTypeID('clearGuides'));

    // Change ALL to NO to disable interactivity when batching
    var interactive = DialogModes.ALL;

    // Add 4 margin guides based on % of canvas size
    // Enter your desired values - top, left, bottom, right
    newGuideLayout(10, 25, 10, 25);

    // Set the rulers to pixels
    app.preferences.rulerUnits = Units.PIXELS;
    
    // Get the existing guides
    var guides = getGuides(doc);

    if (guides.horizontal.length >= 2 && guides.vertical.length >= 2) {
        // Sort the guides to find the outermost guides
        guides.horizontal.sort(function(a, b) { return a - b; });
        guides.vertical.sort(function(a, b) { return a - b; });

        // Define the selection region based on the outermost guides
        var selectionRegion = [
            [guides.vertical[0], guides.horizontal[0]], // Top-left
            [guides.vertical[guides.vertical.length - 1], guides.horizontal[0]], // Top-right
            [guides.vertical[guides.vertical.length - 1], guides.horizontal[guides.horizontal.length - 1]], // Bottom-right
            [guides.vertical[0], guides.horizontal[guides.horizontal.length - 1]] // Bottom-left
        ];

        // Make the selection
        doc.selection.select(selectionRegion);

        // Crop to the selection: delete cropped pixels = true
        cropToSelection(doc.selection.bounds[1], doc.selection.bounds[0], doc.selection.bounds[3], doc.selection.bounds[2], false);

        // Conditionally clear existing guides
        if (app.activeDocument.guides.length > 0)
            app.runMenuItem(stringIDToTypeID('clearGuides'));
        
        // Reset the original ruler units
        app.preferences.rulerUnits = origRulerUnits;

    } else {
        alert("Please make sure there are at least 2 horizontal and 2 vertical guides.");
    }
} else {
    alert("No document is open.");
}


// Functions

function getGuides(doc) {
    var guides = doc.guides;
    var horizontalGuides = [];
    var verticalGuides = [];
    for (var i = 0; i < guides.length; i++) {
        if (guides[i].direction == Direction.HORIZONTAL) {
            horizontalGuides.push(guides[i].coordinate.as('px'));
        } else {
            verticalGuides.push(guides[i].coordinate.as('px'));
        }
    }
    return {horizontal: horizontalGuides, vertical: verticalGuides};
}

function newGuideLayout(marginTop, marginLeft, marginBottom, marginRight) {
    app.preferences.rulerUnits = Units.PERCENT;
    var c2t = function (s) {
        return app.charIDToTypeID(s);
    };
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    var descriptor2 = new ActionDescriptor();
    descriptor.putBoolean(s2t("replace"), true); // true or false boolean
    descriptor.putEnumerated(s2t("presetKind"), s2t("presetKindType"), s2t("presetKindCustom"));
    descriptor2.putUnitDouble(s2t("marginTop"), s2t("percentUnit"), marginTop);
    descriptor2.putUnitDouble(s2t("marginLeft"), s2t("percentUnit"), marginLeft);
    descriptor2.putUnitDouble(s2t("marginBottom"), s2t("percentUnit"), marginBottom);
    descriptor2.putUnitDouble(s2t("marginRight"), s2t("percentUnit"), marginRight);
    descriptor2.putInteger(c2t("GdCA"), 0); // ?
    descriptor2.putInteger(c2t("GdCR"), 74); // Red value
    descriptor2.putInteger(c2t("GdCG"), 255); // Green value
    descriptor2.putInteger(c2t("GdCB"), 255); // Blue value
    descriptor.putObject(s2t("guideLayout"), s2t("guideLayout"), descriptor2);
    descriptor.putEnumerated(s2t("guideTarget"), s2t("guideTarget"), s2t("guideTargetCanvas"));
    executeAction(s2t("newGuideLayout"), descriptor, interactive);
}

function cropToSelection(top, left, bottom, right, retainPixels) {
    // Courtesy of Chuck Uebele
    app.preferences.rulerUnits = Units.PIXELS;
    var idCrop = charIDToTypeID("Crop");
    var desc11 = new ActionDescriptor();
    var idT = charIDToTypeID("T   ");
    var desc12 = new ActionDescriptor();
    var idTop = charIDToTypeID("Top ");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idTop, idPxl, top);
    var idLeft = charIDToTypeID("Left");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idLeft, idPxl, left);
    var idBtom = charIDToTypeID("Btom");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idBtom, idPxl, bottom);
    var idRght = charIDToTypeID("Rght");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idRght, idPxl, right);
    var idRctn = charIDToTypeID("Rctn");
    desc11.putObject(idT, idRctn, desc12);
    var idAngl = charIDToTypeID("Angl");
    var idAng = charIDToTypeID("#Ang");
    desc11.putUnitDouble(idAngl, idAng, 0.000000);
    var idDlt = charIDToTypeID("Dlt ");
    desc11.putBoolean(idDlt, retainPixels); // delete cropped pixels = true | false
    var idcropAspectRatioModeKey = stringIDToTypeID("cropAspectRatioModeKey");
    var idcropAspectRatioModeClass = stringIDToTypeID("cropAspectRatioModeClass");
    var idtargetSize = stringIDToTypeID("targetSize");
    desc11.putEnumerated(idcropAspectRatioModeKey, idcropAspectRatioModeClass, idtargetSize);
    executeAction(idCrop, desc11, DialogModes.NO);
}

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

 

2 replies

Stephen Marsh
Community Expert
Stephen MarshCommunity ExpertCorrect answer
Community Expert
June 12, 2024

 

@Mark37430984r9lw 

 

You can try the following script. It's interactive, once you know what the average/common % margin values are, it is easy to update these values and turn off the interactive step so that this can be batched.

 

EDIT: I have updated the original interactive guide layout code to now work in % without manually setting the units.

 

The script will then crop to the guides and remove the guides. The cropped pixels can be retained or permanently deleted by changing false to true in the crop function call.

 

/*
Canvas Margin Percentage Crop.jsx
v1.2 - 12th June 2024, Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/i-want-to-make-a-form-to-define-crops/td-p/14675929
*/

#target photoshop

// Check if a document is open
if (app.documents.length > 0) {

    // Set the document variable
    var doc = app.activeDocument;

    // Get and set the ruler units
    var origRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PERCENT;

    // Conditionally clear existing guides
    if (app.activeDocument.guides.length > 0)
    app.runMenuItem(stringIDToTypeID('clearGuides'));

    // Change ALL to NO to disable interactivity when batching
    var interactive = DialogModes.ALL;

    // Add 4 margin guides based on % of canvas size
    // Enter your desired values - top, left, bottom, right
    newGuideLayout(10, 25, 10, 25);

    // Set the rulers to pixels
    app.preferences.rulerUnits = Units.PIXELS;
    
    // Get the existing guides
    var guides = getGuides(doc);

    if (guides.horizontal.length >= 2 && guides.vertical.length >= 2) {
        // Sort the guides to find the outermost guides
        guides.horizontal.sort(function(a, b) { return a - b; });
        guides.vertical.sort(function(a, b) { return a - b; });

        // Define the selection region based on the outermost guides
        var selectionRegion = [
            [guides.vertical[0], guides.horizontal[0]], // Top-left
            [guides.vertical[guides.vertical.length - 1], guides.horizontal[0]], // Top-right
            [guides.vertical[guides.vertical.length - 1], guides.horizontal[guides.horizontal.length - 1]], // Bottom-right
            [guides.vertical[0], guides.horizontal[guides.horizontal.length - 1]] // Bottom-left
        ];

        // Make the selection
        doc.selection.select(selectionRegion);

        // Crop to the selection: delete cropped pixels = true
        cropToSelection(doc.selection.bounds[1], doc.selection.bounds[0], doc.selection.bounds[3], doc.selection.bounds[2], false);

        // Conditionally clear existing guides
        if (app.activeDocument.guides.length > 0)
            app.runMenuItem(stringIDToTypeID('clearGuides'));
        
        // Reset the original ruler units
        app.preferences.rulerUnits = origRulerUnits;

    } else {
        alert("Please make sure there are at least 2 horizontal and 2 vertical guides.");
    }
} else {
    alert("No document is open.");
}


// Functions

function getGuides(doc) {
    var guides = doc.guides;
    var horizontalGuides = [];
    var verticalGuides = [];
    for (var i = 0; i < guides.length; i++) {
        if (guides[i].direction == Direction.HORIZONTAL) {
            horizontalGuides.push(guides[i].coordinate.as('px'));
        } else {
            verticalGuides.push(guides[i].coordinate.as('px'));
        }
    }
    return {horizontal: horizontalGuides, vertical: verticalGuides};
}

function newGuideLayout(marginTop, marginLeft, marginBottom, marginRight) {
    app.preferences.rulerUnits = Units.PERCENT;
    var c2t = function (s) {
        return app.charIDToTypeID(s);
    };
    var s2t = function (s) {
        return app.stringIDToTypeID(s);
    };
    var descriptor = new ActionDescriptor();
    var descriptor2 = new ActionDescriptor();
    descriptor.putBoolean(s2t("replace"), true); // true or false boolean
    descriptor.putEnumerated(s2t("presetKind"), s2t("presetKindType"), s2t("presetKindCustom"));
    descriptor2.putUnitDouble(s2t("marginTop"), s2t("percentUnit"), marginTop);
    descriptor2.putUnitDouble(s2t("marginLeft"), s2t("percentUnit"), marginLeft);
    descriptor2.putUnitDouble(s2t("marginBottom"), s2t("percentUnit"), marginBottom);
    descriptor2.putUnitDouble(s2t("marginRight"), s2t("percentUnit"), marginRight);
    descriptor2.putInteger(c2t("GdCA"), 0); // ?
    descriptor2.putInteger(c2t("GdCR"), 74); // Red value
    descriptor2.putInteger(c2t("GdCG"), 255); // Green value
    descriptor2.putInteger(c2t("GdCB"), 255); // Blue value
    descriptor.putObject(s2t("guideLayout"), s2t("guideLayout"), descriptor2);
    descriptor.putEnumerated(s2t("guideTarget"), s2t("guideTarget"), s2t("guideTargetCanvas"));
    executeAction(s2t("newGuideLayout"), descriptor, interactive);
}

function cropToSelection(top, left, bottom, right, retainPixels) {
    // Courtesy of Chuck Uebele
    app.preferences.rulerUnits = Units.PIXELS;
    var idCrop = charIDToTypeID("Crop");
    var desc11 = new ActionDescriptor();
    var idT = charIDToTypeID("T   ");
    var desc12 = new ActionDescriptor();
    var idTop = charIDToTypeID("Top ");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idTop, idPxl, top);
    var idLeft = charIDToTypeID("Left");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idLeft, idPxl, left);
    var idBtom = charIDToTypeID("Btom");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idBtom, idPxl, bottom);
    var idRght = charIDToTypeID("Rght");
    var idPxl = charIDToTypeID("#Pxl");
    desc12.putUnitDouble(idRght, idPxl, right);
    var idRctn = charIDToTypeID("Rctn");
    desc11.putObject(idT, idRctn, desc12);
    var idAngl = charIDToTypeID("Angl");
    var idAng = charIDToTypeID("#Ang");
    desc11.putUnitDouble(idAngl, idAng, 0.000000);
    var idDlt = charIDToTypeID("Dlt ");
    desc11.putBoolean(idDlt, retainPixels); // delete cropped pixels = true | false
    var idcropAspectRatioModeKey = stringIDToTypeID("cropAspectRatioModeKey");
    var idcropAspectRatioModeClass = stringIDToTypeID("cropAspectRatioModeClass");
    var idtargetSize = stringIDToTypeID("targetSize");
    desc11.putEnumerated(idcropAspectRatioModeKey, idcropAspectRatioModeClass, idtargetSize);
    executeAction(idCrop, desc11, DialogModes.NO);
}

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

 

Inspiring
June 12, 2024

Many thanks!

I'll reply again once I try this out.

Stephen Marsh
Community Expert
Community Expert
June 12, 2024
quote

Many thanks!

I'll reply again once I try thsi out.


By @Mark37430984r9lw

 

You're welcome, I have updated the code to a 1.2 version as there were some unexpected results in the crop with different ruler units. I believe that I have now overcome this issue.

Stephen Marsh
Community Expert
Community Expert
June 12, 2024
quote

Instead of playing around with the crop tool, can I have a form allow me to define the percentage of the top, bottom, left, and right to crop.  I’d then like to be able to click on a button that will define the subject and either perform the crop or outline it for me to review before proceeding. 

There are going to be times when the bird is so surrounded by vegetation that Photoshop will not be able to correctly define the subject, so in those cases, I would need to be able to use a tool to draw around the bird to define it.

Also, with a fixed percentage definition, can I then batch files with the same crop?


By @Mark37430984r9lw

 

The crop tool is generally for visual cropping with options such as ratio, rule of thirds grid etc.

 

What you describe isn't a standard feature. An action, or better yet – a script could be used to do some of what you are asking for without a preview.

 

Why would using arbitrary % values for left/top/right/bottom be any better than the standard crop tool, or a Select All followed by Select > Transform Selection and then Image > Crop?

 

Can the Select > Select Subject command successfully and consistently identify the bird as the area of interest?

 

You mention both interactive and batch cropping. Do you want the batch to be interactive or fully automated?

 

It would help if you could provide before/after example images to illustrate.

 

Much of your post sounds like an idea/feature request for future development, however, that would require a better description of the process and or mock-up illustrations of the process.

Inspiring
June 12, 2024

I just realized I've been using the rectangular marquee tool for crops, not the crop tool.  Not certain whether this makes a difference with the discussion.

I just tried selecting the subject with an original bird photo where the bird was small in the frame and Photoshop had no idea what the subject was.  I then selected the subject from the same pic that I had cropped, and it selected only the subject.

Maybe if I elaborate my intentions, somebody can lead me to some PS functionality that already exists.  I learned from an instructional video that if a bird is facing a certain direction, a photo looks much better with a tight crop on the opposite side with more space in the direction the bird is facing/looking.  My personal preference is to crop a little more tightly on the top than the bottom.  I do a reasonable job of hand cropping to these specifications, but this takes too long.  I’d like to come up with some “sweet spot” formulas on how much to crop each side.  I’m using the plural here, as I realize that the ideal percentages are going to vary depending on how much the bird is in frame, and how centered it originally was.  So, if I could use some kind of form or even a script to try some formulas and look at the results, this would save me a lot of time and effort.  I don’t want to temporarily isolate the bird and the background, and then put the subject back in a different part of the background as part of the process.  I submit photos for scientific research, and this would artificially change the natural environment that I’m not supposed to edit out of photos.

Regarding batch processing, I’d like it automated.  I usually have a lot of similar burst shots, but the subject may have different poses, and the lighting may be better for some.  It would save a ton of time if I could apply a crop formula to the entire batch at once.  BUT, in instances where PS cannot find the subject, I realize this would not be feasible.

I’m very new to all of this.  I’ve only been using full photoshop for three weeks now. I find it unlikely that PS does not have any built-in tools to accomplish what I want.  Maybe some third-party plugin is what I need.  I’m certain there are industry professionals, or much more experienced post-processors than I who use mathematical cropping formulas within Photoshop all of the time.