Getting adjustment values from adjustment layers

Oh, you’re too kind :smiley:

@Pierre_G i think i need your help again.
I’ve got it all running fine so far but now i ran into a problem with layers that have channels. I guess that is what @Jarda meant.
The problem is that when you create a levels layer with following batchplay code:

await batchPlay(

         [{

            "_obj": "make",

            "_target": [{

               "_ref": "adjustmentLayer"

            }],

            "using": {

               "_obj": "adjustmentLayer",

               "name": "Levels",

               "color": {

                  "_enum": "color",

                  "_value": "orange"

               },

               "type": {

                  "_obj": "levels",

                  "presetKind": {

                     "_enum": "presetKindType",

                     "_value": "presetKindDefault"

                  }

               }

            }

         },{

            "_obj": "set",

            "_target": [{

               "_ref": "adjustmentLayer",

               "_enum": "ordinal",

               "_value": "targetEnum"

            }],

            "to": {

               "_obj": "levels",

               "adjustment": [{

                  "_obj": "levelsAdjustment",

                  "channel": {

                     "_ref": "channel",

                     "_enum": "channel",

                     "_value": "composite"

                  },

                  "gamma": 1

               }, {

                  "_obj": "levelsAdjustment",

                  "channel": {

                     "_ref": "channel",

                     "_enum": "channel",

                     "_value": "red"

                  },

                  "input": [

                     0,

                     255

                  ],

                  "gamma": 1

               }, {

                  "_obj": "levelsAdjustment",

                  "channel": {

                     "_ref": "channel",

                     "_enum": "channel",

                     "_value": "grain"

                  },

                  "input": [

                     0,

                     255

                  ],

                  "gamma": 1

               }, {

                  "_obj": "levelsAdjustment",

                  "channel": {

                     "_ref": "channel",

                     "_enum": "channel",

                     "_value": "blue"

                  },

                  "input": [

                     0,

                     255

                  ],

                  "gamma": 1

               }]

            }

         }], {

            "synchronousExecution": false,

            "modalBehavior": "execute"

         })

   }

}

You should think now that you’ll find all channels adjustments in the json layer info. But it’s just not there. That means when reading the current layer info like so:

const result = await batchPlay(

      [{

         "_obj": "get",

         "_target": [{

            "_property": "json"

         }, {

            "_ref": "layer",

            "_enum": "ordinal",

            "_value": "targetEnum"

         }],

         "_options": {}

      }], {

         "synchronousExecution": true,

         "modalBehavior": "execute"

      });

   const layerInfo = await result[0].json;

try{

      const channelData = await JSON.parse(layerInfo).layers["0"].adjustment.adjustment

      console.log(Object.values(channelData))
      const getCompositeGamma = parse[0].gamma
      console.log("Gamma value: " + getCompositeGamma))


}catch(e){

   console.log(Levels has no adjustments")

}

…You won’t get any adjustment data because there is none. That said, it seems like PS creates the array with adjustments only then, when the user makes adjustments with an offset to the standard values.
I even tried to work around that with creating a levels layer, then making adjustments with batchplay and reset the adjustments again to standard values for always having four channels, but PS will then delete the array again.
Now that brings me to checking if a layer has channel adjustments with try/catch(e). And it lead me down to “checking hell”. Because when a levels layer has adjustments for all four channels, everything is fine. Channel composite is then 0, Channel red is 1, Channel green is 2 and channel blue is 3… But when a User only made adjustments to channel red it will be 0. On another layer Channel blue may be 0 and so on. And to take this even further we got nested arrays for black and white points…
However, i tried to wrap my head around this in the last three days and i can’t find a solution for this.

My 2 cents…
You can change adjustment in the same make action by skipping presetKind or using value of presetKindCustom and adding adjustments. Something like:

            "using": {
               "_obj": "adjustmentLayer",
               "name": "Levels",
               "color": {
                  "_enum": "color",
                  "_value": "orange"
               },
               "type": {
                  "_obj": "levels",
                  "adjustment": [{
                     "_obj": "levelsAdjustment",
                     "channel": {
                        "_ref": "channel",
                        "_enum": "channel",
                        "_value": "composite"
                     },
                     "gamma": 1
                  }, {
                     "_obj": "levelsAdjustment",
                     "channel": {
                        "_ref": "channel",
                        "_enum": "channel",
                        "_value": "red"
                     },
                     "input": [0, 255],
                     "gamma": 1
                  }]
               }
            }

@Karmalakas thanks for your suggestion!
i tried with

batchPlay(

         [{

            "_obj": "make",

            "_target": [{

               "_ref": "adjustmentLayer"

            }],

            "using": {

               "_obj": "adjustmentLayer",

               "name": "Levels",

               "color": {

                  "_enum": "color",

                  "_value": "orange"

               },

               "type": {

                  "_obj": "levels",

                  "adjustment": [{

                     "_obj": "levelsAdjustment",

                     "channel": {

                        "_ref": "channel",

                        "_enum": "channel",

                        "_value": "composite"

                     },

                     "input": [0, 255],

                     "gamma": 1

                  }, {

                     "_obj": "levelsAdjustment",

                     "channel": {

                        "_ref": "channel",

                        "_enum": "channel",

                        "_value": "red"

                     },

                     "input": [0, 255],

                     "gamma": 1

                  }, {

                     "_obj": "levelsAdjustment",

                     "channel": {

                        "_ref": "channel",

                        "_enum": "channel",

                        "_value": "green"

                     },

                     "input": [0, 255],

                     "gamma": 1

                  }, {

                     "_obj": "levelsAdjustment",

                     "channel": {

                        "_ref": "channel",

                        "_enum": "channel",

                        "_value": "blue"

                     },

                     "input": [0, 255],

                     "gamma": 1

                  }]

               }

            }

         }], {

            "synchronousExecution": false,

            "modalBehavior": "execute"

         });

…I changed blue gamma and after reading layer info it only gave me gamma in the blue channel (i now expected to have all four channels). Maybe i missed something? Below the json result:

{"version":"1.6.1","timeStamp":0.000,"count":0,"id":220,"file":"C:\\Users\\Admin\\Pictures\\raw00166.dng","bounds":{"top":0,"left":0,"bottom":1747,"right":1313},"selection":[1],"resolution":300,"globalLight":{"angle":30,"altitude":30},"generatorSettings":false,"profile":"Adobe RGB (1998)","mode":"RGBColor","depth":16,"layers":[{"id":42,"index":1,"type":"adjustmentLayer","name":"Levels","bounds":{"top":0,"left":0,"bottom":1747,"right":1313},"visible":true,"clipped":false,"adjustment":{"presetKind":"presetKindCustom","adjustment":[{"channel":"blue","gamma":1.14}],"class":"levels"},"mask":{"bounds":{"top":0,"left":0,"bottom":1747,"right":1313},"extendWithWhite":true},"generatorSettings":false}]}

Sorry, can’t say much about getter :frowning: I just noticed you’re creating the layer and then setting adjustments in two separate descriptors, so I thought I’d suggest to merge them to a single one. I don’t believe this affects how data is returned later for getter, but as I said - not much help from me here :slight_smile:

1 Like

I thought as well “this could be IT!”…but naaah… :frowning:

Hi,
you are correct, in terms of Ps mechanism.
However, there are a few point that needs to be addressed here:

  • the code that you’re using to create the Levels adj. layer in fact sets values, but default ones, so entries in the json item won’t be created. If you really want to create something, then you’d introduce a tiny delta in the gamma for example:

Setter:

// makeLevels
document.getElementById("makeLevels").addEventListener("click", makeLevels);
async function makeLevels() {
  await batchPlay(
    [{
      "_obj": "make",
      "_target": [{
        "_ref": "adjustmentLayer"
      }],
      "using": {
        "_obj": "adjustmentLayer",
        "name": "Levels Test",
        "color": {
          "_enum": "color",
          "_value": "orange"
        },
        "type": {
          "_obj": "levels",
          "presetKind": {
            "_enum": "presetKindType",
            "_value": "presetKindDefault"
          }
        }
      }
    }, {
      "_obj": "set",
      "_target": [{
        "_ref": "adjustmentLayer",
        "_enum": "ordinal",
        "_value": "targetEnum"
      }],
      "to": {
        "_obj": "levels",
        "adjustment": [{
          "_obj": "levelsAdjustment",
          "channel": {
            "_ref": "channel",
            "_enum": "channel",
            "_value": "composite"
          },
          "gamma": 1.01
        }, {
          "_obj": "levelsAdjustment",
          "channel": {
            "_ref": "channel",
            "_enum": "channel",
            "_value": "red"
          },
          "input": [
            0,
            255
          ],
          "gamma": 1.01
        }, {
          "_obj": "levelsAdjustment",
          "channel": {
            "_ref": "channel",
            "_enum": "channel",
            "_value": "grain"
          },
          "input": [
            0,
            255
          ],
          "gamma": 1.01
        }, {
          "_obj": "levelsAdjustment",
          "channel": {
            "_ref": "channel",
            "_enum": "channel",
            "_value": "blue"
          },
          "input": [
            0,
            255
          ],
          "gamma": 1.01
        }]
      }
    }], {
      "synchronousExecution": false,
      "modalBehavior": "execute"
    })
}

Not sure in which case you’d use that, though.

  • Further, the second code you’ve posted is a bit buggy.
    Here’s a fixed version:

Getter v1

// getLevels
document.getElementById("getLevels").addEventListener("click", getLevels);
async function getLevels() {
  const result = await batchPlay(
    [{
      "_obj": "get",
      "_target": [{
        "_property": "json"
      }, {
        "_ref": "layer",
        "_enum": "ordinal",
        "_value": "targetEnum"
      }],
      "_options": {}
    }], {
      "synchronousExecution": true,
      "modalBehavior": "execute"
    });

  const layerInfo = result[0].json;
  try {
    const channelData = JSON.parse(layerInfo).layers["0"].adjustment.adjustment;
    console.log(Object.values(channelData));
    const getCompositeGamma = channelData[0].gamma;
    console.log("Gamma value: " + getCompositeGamma);
  } catch (e) {
    console.error(e.stack);
    // console.log("Levels has no adjustments");
  }
}

Notes:

  • you don’t need await JSON;
  • parse[0].gamma has to be channelData[0].gamma;
  • catch (e) : you might want to use console.error(e.stack) at least you’d know what is going on and some details on the caught error

Now you’d be able to retrieve the current layer details.

  • However, you can take things a bit further :slight_smile:

Getter v2

const app = require('photoshop').app;
const action = require("photoshop").action;
const batchPlay = require("photoshop").action.batchPlay;
let currentDocument = app.activeDocument;
let layers = currentDocument.activeLayers;
let theActiveLayer;
let arrayLevels = [];

// getLevels
document.getElementById("getLevels").addEventListener("click", getLevels);
async function getLevels() {

  theActiveLayer = currentDocument.activeLayers;
  let currentLayerID = theActiveLayer[0]._id; // get the current layer ID

  const result = await batchPlay(
    [{
      "_obj": "get",
      "_target": [{
          "_property": "json"
        },
        {
          "_ref": "layer",
          "_id": currentLayerID // get json of currentLayerID
        },
        {
          "_ref": "document",
          "_enum": "ordinal",
          "_value": "targetEnum"
        }
      ],
      "_options": {}
    }], {
      "synchronousExecution": true,
    });

  let jsonProp = result[0].json;
  console.log("jsonProp", jsonProp)

  let jsonData = JSON.parse(jsonProp);
  console.log("jsonData", jsonData)

  try {
    const channelData = jsonData.layers["0"].adjustment.adjustment;
    if (channelData == undefined) return console.warn("Levels has no adjustments");

    const channelCount = jsonData.layers["0"].adjustment.adjustment.length;
    const getCompositeGamma = channelData[0].gamma;
    // if you wan to show info:
    // console.log("channelCount", channelCount, Object.values(channelData));
    // console.log("Gamma value: ", getCompositeGamma);

    arrayLevels = [];
    for (var i = 0; i < channelCount; i++) {
      console.log(`Channel#${i} is: "${channelData[i].channel}", Gamma = ${channelData[i].gamma}`); // ie values to be fetched
      arrayLevels.push([channelData[i].channel, channelData[i].gamma])
    }
  } catch (e) {
    console.error(e.stack); // show why the catch
  }
}

There are many ways to optimise the above Getter code, of course. I decided to keep it as simple as possible for readability. Perhaps you could add a check for the kind of layer you’re on. You could also use an array like arrayLevels to store parameters found in each channel(s)… etc. :wink:

Hope this helps !

1 Like

Pierre! That helped A LOT! Thank you so much…AGAIN!