9 Replies Latest reply on Mar 20, 2011 4:16 PM by Michael L Hale

    determine if layer is black and white (or desaturated)

    JJ Fulks Level 1

      Is there a way to programmatically determine if a layer of a PSD is black and white?

        • 1. Re: determine if layer is black and white (or desaturated)
          Michael L Hale Level 5

          I can think of several ways. Here is one.

           

          function newDocFromLayer(newDocName,newLayerName){
              var desc = new ActionDescriptor();
                  var ref = new ActionReference();
                  ref.putClass( charIDToTypeID( "Dcmn" ) );
              desc.putReference( charIDToTypeID( "null" ), ref );
              desc.putString( charIDToTypeID( "Nm  " ), newDocName );
                  var ref2 = new ActionReference();
                  ref2.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
              desc.putReference( charIDToTypeID( "Usng" ), ref2 );
              desc.putString( charIDToTypeID( "LyrN" ), newLayerName );
               executeAction( charIDToTypeID( "Mk  " ), desc, DialogModes.NO );
               return app.activeDocument;
          };
          var tempDoc = newDocFromLayer('newDocName','newLayerName');
          tempDoc.changeMode(ChangeMode.LAB);
          if(!tempDoc.activeLayer.isBackgroundLayer) tempDoc.trim(TrimType.TRANSPARENT);
          var h = tempDoc.height.as('px');
          var w = tempDoc.width.as('px');
          var totalPixels = w*h;
          var aMidCount = tempDoc.channels[1].histogram[128];
          var bMidCount = tempDoc.channels[2].histogram[128];
          var isGrayscale = ((totalPixels == aMidCount)&&(totalPixels==bMidCount));
          tempDoc.close(SaveOptions.DONOTSAVECHANGES);
          alert(isGrayscale);
          
          • 2. Re: determine if layer is black and white (or desaturated)
            c.pfaffenbichler Level 9

            What mode are the images you want to check in anyway? CMYK, RGB …?

             

            I suppose there are several options to achieve it.

            • Iterating through the pixels with a ColorSampler would take too long for larger images, I guess.

            • The Histogram would be unreliable because the numbers for each value in each Channel could accidentally be identical.

            • But hiding other Layers, duplicating the image (mergeLayersOnly), changing to L*a*b, reading the a- and b-Histograms, closing the copy, un-hiding other Layers should work.

            If the a- and b-Histograms are all bunched up around 127 (some tolerance could be set) it should be a desaturated Layer.

             

             

            // check layer for 0 saturation;
            // 2011, use it at your own risk;
            #target photoshop;
            if (app.documents.length > 0) {
            var myDocument = app.activeDocument;
            hideShowOthers ();
            // duplicate;
            var theCopy = myDocument.duplicate("copy", true);
            theCopy.changeMode(ChangeMode.LAB);
            // get histograms;
            var theA = theCopy.channels[1].histogram;
            var theB = theCopy.channels[2].histogram;
            // check histrograms;
            var check = true;
            check = checkHistogram (theA, check, 0);
            check = checkHistogram (theB, check, 0);
            // close;
            theCopy.close(SaveOptions.DONOTSAVECHANGES);
            // re-show;
            hideShowOthers ();
            if (check == true) {alert ("layer is desaturated")}
            else{alert ("layer contains color")}
            };
            ////// check histogram for cumulation around 227 //////
            function checkHistogram (histogram, check, tolerance) {
                 for (var m = 0; m < 126 - tolerance; m++) {
                      if (histogram[m] != 0) {check =  false}
                      };
                 for (var n = 255; m > 127 + tolerance; m--) {
                      if (histogram[m] != 0) {check =  false}
                      };
                 return check
                 };
            ////// hide/show all others //////
            function hideShowOthers () {
            // =======================================================
            var idShw = charIDToTypeID( "Shw " );
                var desc2 = new ActionDescriptor();
                var idnull = charIDToTypeID( "null" );
                    var list1 = new ActionList();
                        var ref1 = new ActionReference();
                        var idLyr = charIDToTypeID( "Lyr " );
                        var idOrdn = charIDToTypeID( "Ordn" );
                        var idTrgt = charIDToTypeID( "Trgt" );
                        ref1.putEnumerated( idLyr, idOrdn, idTrgt );
                    list1.putReference( ref1 );
                desc2.putList( idnull, list1 );
                var idTglO = charIDToTypeID( "TglO" );
                desc2.putBoolean( idTglO, true );
            executeAction( idShw, desc2, DialogModes.NO );
            };
            

             

             

            But someone else may have a better solution ready.

            1 person found this helpful
            • 3. Re: determine if layer is black and white (or desaturated)
              Michael L Hale Level 5

              Here is another way that seems to be a little faster.

               

              function isGrayscale(){
                   var redHist = activeDocument.channels[0].histogram;
                   var greenHist = activeDocument.channels[1].histogram;
                   var blueHist = activeDocument.channels[2].histogram;
                   for(var h = 0;h < 256; h++){
                        if(!(redHist[h]==greenHist[h] && greenHist[h]==blueHist[h])) return false;
                   }
                   return true;
              };
              function toggleOtherLayers(){//activeLayer
                  var desc = new ActionDescriptor();
                          var ref = new ActionReference();
                          var list = new ActionList();
                          ref.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID("Ordn" ), charIDToTypeID( "Trgt" ) );
                      list.putReference( ref );
                  desc.putList( charIDToTypeID( "null" ), list );
                  desc.putBoolean( charIDToTypeID( "TglO" ), true );
                   executeAction( charIDToTypeID( "Shw " ), desc, DialogModes.NO );
              };
              toggleOtherLayers();
              var isGray = isGrayscale();
              toggleOtherLayers();
              alert(isGray);
              
              1 person found this helpful
              • 4. Re: determine if layer is black and white (or desaturated)
                c.pfaffenbichler Level 9

                Hadn’t noticed your first post (as it took me some time to write my post).

                 

                I think your second method could theoretically produce incorrect results with some images.

                • 5. Re: determine if layer is black and white (or desaturated)
                  Michael L Hale Level 5

                  I could be wrong but I would think that an image would have to be contrived for the histogram of all three channels to match and still not be grayscale.

                  • 6. Re: determine if layer is black and white (or desaturated)
                    c.pfaffenbichler Level 9

                    True, with photographic or painted images the probability of identical numbers for colorful content seems very low indeed …

                     

                    One issue I noticed is that the toggling of the Layers should maybe better be wrapped in an if- or try-clause as files with only a Background Layer might pose a problem otherwise.

                    • 7. Re: determine if layer is black and white (or desaturated)
                      thebestcpu Level 1

                      Here is another conceptual way to do this leveraging the approaches already mentioned.  This works for RGB

                       

                      This checks for R=G=B for all pixels quickly

                       

                      1) Do a difference blend between R channel and G channel

                      2) Check histogram to verify all values are at histogram location 0  (meaning all R and G pixels are identical)

                      3) Do same as steps 1 for G and B channels

                      4) If this too has all histograms values in location 0, then R=G=B at all pixels.

                       

                      Now, if you want to check for low saturation instead of exact R=G=B (especially in 16 bit color mode) then you test for values in the historgram are all in the "lower bins" set by some arbitrary threshold.

                       

                      Leveraging the power of Blends

                       

                       

                      Of course, I am assuming that the OP did not just want to know if the color mode was grayscale.  They have not checked back in.

                      • 8. Re: determine if layer is black and white (or desaturated)
                        JJ Fulks Level 1

                        Thanks Michael, c.pfaffenbichler, and thebestcpu. Your ideas are all very helpful, and just goes to show "there's more than one way to skin a cat" . As I'm planning for my particular application, I was thinking along the lines of what c.pfaffenbichler guessed would not be a good idea; iterating through the pixels of an image. But, each of the ideas you three have offered would be much more efficient.

                         

                        I should have clarified in the original post that the images could be RBG or grayscale, and the test would be performed on both 8-bit and 16-bit images. Even without much experience with ES, I imagine determing if an image is in grayscale color mode is far less complex than determing if an image in RGB color mode is a black and white image, though, correct?

                         

                        Thanks again, I sincerely appreciate the examples.

                        • 9. Re: determine if layer is black and white (or desaturated)
                          Michael L Hale Level 5

                          Yes, you can use document.mode to determine the color mode of a document.  It is only when the image only appears grayscale in one of the color modes do you need the kinds of tests that are in this thread.

                           

                          But I agree that, unless your images are very small, testing the color of each pixel is not something you want to do with javascript.