Skip to main content
leol30
Known Participant
October 8, 2023
Answered

Script to find colour in "black & white" jpgs

  • October 8, 2023
  • 3 replies
  • 3954 views

Hi,

 

I regularly want to sort a folder of JPGs that visually look black and white but may contain colour pixels. Is there a Bridge script available that can check each JPG and move any JPGs that have any colour pixels to another folder.

 

My scripting skills are close to zero but I have been able to customise existing scripts to my own needs if provided with a script that is similar to my needs. Unfortunately, searching the Photoshop and Bridge communities I haven’t been able to find a script that is likely to help.

 

Thanks in advance, any help that can be provided will be much appreciated.

 

Cheers 

 

Leo

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

@leol30 

 

EDIT: Script updated to version 1.1 where files that fail the test for neutrality are moved to a "Rejected" folder.

 

This Photoshop script will process the root/top-level of an input folder for supported files and will log files to the desktop in a text file that have a Standard Deviation value for the sum of the a+b channels that is greater than 0.00 (0.00 being fully neutral).

 
You can adjust the tolerance/threshold for neutral values from the default 0.00 by adjusting the following line of code:
 
if (theValue > 0.00) {
 
This script is more a proof of concept than anything, but it may be useful if you can come up with a suitable Std. Dev. value that accounts for the colour artefacts introduced by JPEG compression. The Mean or Median values of combined or single a & b channels could also be used instead.

 

If this does prove useful I am happy to make changes to the script.

 

P.S. I am more comfortable scripting Photoshop than Bridge, which is why this is a Photoshop script, despite this topic being on in the Bridge forum.

 

Here is the full script for Photoshop:

 

/*
Batch Log Non-Neutral B&W Files from Input Folder.jsx
https://community.adobe.com/t5/bridge-discussions/script-to-find-colour-in-quot-black-amp-white-quot-jpgs/td-p/14141472
v1.1 - 11th October 2023, Stephen Marsh

Info:
This Photoshop script will process the root/top-level of an input folder
for supported files and will log files to the desktop in a text file
that have a Standard Deviation value for the sum of the a+b channels
that is greater than 0.00 (0.00 being fully neutral). A new folder
named "Rejected" will be created in the input folder and documents
which fail will be moved to this folder.
*/

#target photoshop

if (app.documents.length === 0) {

    // Select the folder containing the files
    var inputFolder = Folder.selectDialog("Select the input folder", "");

    // Add or remove supported file extensions as required
    var inputFiles = inputFolder.getFiles(/\.(jpg|jpeg|tif|tiff|psd|psb|png|bmp|webp|tga)$/i);
    inputFiles.sort();

    // Set the input file counter
    var inputFileCounter = 0;

    // Set the failed file counter
    var failedCounter = 0;

    // Initial log file entries
    var dateTime = new Date().toLocaleString();
    var logFile = new File("~/Desktop/Neutral File Evaluation.log");
    if (logFile.exists)
        logFile.remove();
    var os = $.os.toLowerCase().indexOf("mac") >= 0 ? "mac" : "windows";
    if (os === "mac") {
        var logFileLF = "Unix";
    } else {
        logFileLF = "Windows";
    }
    logFile.open("w");
    logFile.encoding = "UTF-8";
    logFile.lineFeed = logFileLF;
    logFile.writeln(dateTime);
    logFile.writeln(inputFolder.fsName + "/" + "Rejects" + "/");
    logFile.close();

    // Process the files
    for (var i = 0; i < inputFiles.length; i++) {
        open(inputFiles[i]);
        
        // Convert to Lab mode
        activeDocument.changeMode(ChangeMode.LAB);
        
        // Histogram evaluation
        var aCha = theHistProps(activeDocument.channels[1].histogram);
        var bCha = theHistProps(activeDocument.channels[2].histogram);
        // theMean = [0] | theMedian = [1] | theStandDev = [2]
        var theValue = (((aCha[2] + bCha[2]) / 2).toFixed(2));
        
        /* Change neutral target value as required */
        if (theValue > 0.00) {

            // Rejected files variables and folder
            var theDoc = activeDocument;
            var theName = theDoc.name;
            var theFolder = Folder(theDoc.path + "/" + "Rejects");
            if (!theFolder.exists) {
                theFolder.create()
            }

            try {
                writeFailedLogEntry();
                failedCounter++;
                
                // Copy the rejected files
                File(theDoc.fullName).copy(theFolder + "/" + theName);
                
                // Remove the original files
                theDoc.fullName.remove();

            } catch (e) {}
            // Close without saving
            activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        } else {
            // Close without saving
            activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        }
        inputFileCounter++;
    }

    // End of script notification
    alert(inputFileCounter + " files processed:" + "\r" + failedCounter + " files failed");

} else {
    alert('No documents should be open when running this script!');
}


///// FUNCTIONS /////

function writeFailedLogEntry() {
    var os = $.os.toLowerCase().indexOf("mac") >= 0 ? "mac" : "windows";
    if (os === "mac") {
        var logFileLF = "Unix";
    } else {
        logFileLF = "Windows";
    }
    logFile.open("a");
    logFile.encoding = "UTF-8";
    logFile.lineFeed = logFileLF;
    logFile.writeln("FAILED = (" + theValue + ") " + activeDocument.name + "\r");
    logFile.close();
}

function theHistProps(theHist) {
    /* Based on https://community.adobe.com/t5/photoshop/how-to-get-the-histogram-s-std-dev/td-p/9875041 */
    // get total number;
    var thePixels = 0;
    for (var m = 0; m < theHist.length; m++) {
        thePixels = thePixels + theHist[m];
    }
    // get mean and median
    var theMean = 0;
    var aTotal = 0;
    var check = false;
    for (var n = 0; n < theHist.length; n++) {
        theMean = theMean + (n * theHist[n] / thePixels);
        aTotal = aTotal + theHist[n];
        if (aTotal >= thePixels / 2 && check === false) {
            theMedian = n;
            check = true;
        }
    }
    // get standard deviation
    var theStandDev = 0;
    for (var o = 0; o < theHist.length; o++) {
        theStandDev = theStandDev + (Math.pow((o - theMean), 2) * theHist[o]);
    }
    theStandDev = Math.sqrt(theStandDev / thePixels);
    // Return the properties: theMean = [0] | theMedian = [1] | theStandDev = [2]
    return ([theMean, theMedian, theStandDev]);
}

 

  1. Copy the code text to the clipboard
  2. Open a new blank file in a plain-text editor (not in a word processor)
  3. Paste the code in
  4. Save as a plain text format file – .txt
  5. Rename the saved file extension from .txt to .jsx
  6. Install or browse to the .jsx file to run (see below)

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

 

3 replies

Stephen Marsh
Community Expert
Stephen MarshCommunity ExpertCorrect answer
Community Expert
October 11, 2023

@leol30 

 

EDIT: Script updated to version 1.1 where files that fail the test for neutrality are moved to a "Rejected" folder.

 

This Photoshop script will process the root/top-level of an input folder for supported files and will log files to the desktop in a text file that have a Standard Deviation value for the sum of the a+b channels that is greater than 0.00 (0.00 being fully neutral).

 
You can adjust the tolerance/threshold for neutral values from the default 0.00 by adjusting the following line of code:
 
if (theValue > 0.00) {
 
This script is more a proof of concept than anything, but it may be useful if you can come up with a suitable Std. Dev. value that accounts for the colour artefacts introduced by JPEG compression. The Mean or Median values of combined or single a & b channels could also be used instead.

 

If this does prove useful I am happy to make changes to the script.

 

P.S. I am more comfortable scripting Photoshop than Bridge, which is why this is a Photoshop script, despite this topic being on in the Bridge forum.

 

Here is the full script for Photoshop:

 

/*
Batch Log Non-Neutral B&W Files from Input Folder.jsx
https://community.adobe.com/t5/bridge-discussions/script-to-find-colour-in-quot-black-amp-white-quot-jpgs/td-p/14141472
v1.1 - 11th October 2023, Stephen Marsh

Info:
This Photoshop script will process the root/top-level of an input folder
for supported files and will log files to the desktop in a text file
that have a Standard Deviation value for the sum of the a+b channels
that is greater than 0.00 (0.00 being fully neutral). A new folder
named "Rejected" will be created in the input folder and documents
which fail will be moved to this folder.
*/

#target photoshop

if (app.documents.length === 0) {

    // Select the folder containing the files
    var inputFolder = Folder.selectDialog("Select the input folder", "");

    // Add or remove supported file extensions as required
    var inputFiles = inputFolder.getFiles(/\.(jpg|jpeg|tif|tiff|psd|psb|png|bmp|webp|tga)$/i);
    inputFiles.sort();

    // Set the input file counter
    var inputFileCounter = 0;

    // Set the failed file counter
    var failedCounter = 0;

    // Initial log file entries
    var dateTime = new Date().toLocaleString();
    var logFile = new File("~/Desktop/Neutral File Evaluation.log");
    if (logFile.exists)
        logFile.remove();
    var os = $.os.toLowerCase().indexOf("mac") >= 0 ? "mac" : "windows";
    if (os === "mac") {
        var logFileLF = "Unix";
    } else {
        logFileLF = "Windows";
    }
    logFile.open("w");
    logFile.encoding = "UTF-8";
    logFile.lineFeed = logFileLF;
    logFile.writeln(dateTime);
    logFile.writeln(inputFolder.fsName + "/" + "Rejects" + "/");
    logFile.close();

    // Process the files
    for (var i = 0; i < inputFiles.length; i++) {
        open(inputFiles[i]);
        
        // Convert to Lab mode
        activeDocument.changeMode(ChangeMode.LAB);
        
        // Histogram evaluation
        var aCha = theHistProps(activeDocument.channels[1].histogram);
        var bCha = theHistProps(activeDocument.channels[2].histogram);
        // theMean = [0] | theMedian = [1] | theStandDev = [2]
        var theValue = (((aCha[2] + bCha[2]) / 2).toFixed(2));
        
        /* Change neutral target value as required */
        if (theValue > 0.00) {

            // Rejected files variables and folder
            var theDoc = activeDocument;
            var theName = theDoc.name;
            var theFolder = Folder(theDoc.path + "/" + "Rejects");
            if (!theFolder.exists) {
                theFolder.create()
            }

            try {
                writeFailedLogEntry();
                failedCounter++;
                
                // Copy the rejected files
                File(theDoc.fullName).copy(theFolder + "/" + theName);
                
                // Remove the original files
                theDoc.fullName.remove();

            } catch (e) {}
            // Close without saving
            activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        } else {
            // Close without saving
            activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        }
        inputFileCounter++;
    }

    // End of script notification
    alert(inputFileCounter + " files processed:" + "\r" + failedCounter + " files failed");

} else {
    alert('No documents should be open when running this script!');
}


///// FUNCTIONS /////

function writeFailedLogEntry() {
    var os = $.os.toLowerCase().indexOf("mac") >= 0 ? "mac" : "windows";
    if (os === "mac") {
        var logFileLF = "Unix";
    } else {
        logFileLF = "Windows";
    }
    logFile.open("a");
    logFile.encoding = "UTF-8";
    logFile.lineFeed = logFileLF;
    logFile.writeln("FAILED = (" + theValue + ") " + activeDocument.name + "\r");
    logFile.close();
}

function theHistProps(theHist) {
    /* Based on https://community.adobe.com/t5/photoshop/how-to-get-the-histogram-s-std-dev/td-p/9875041 */
    // get total number;
    var thePixels = 0;
    for (var m = 0; m < theHist.length; m++) {
        thePixels = thePixels + theHist[m];
    }
    // get mean and median
    var theMean = 0;
    var aTotal = 0;
    var check = false;
    for (var n = 0; n < theHist.length; n++) {
        theMean = theMean + (n * theHist[n] / thePixels);
        aTotal = aTotal + theHist[n];
        if (aTotal >= thePixels / 2 && check === false) {
            theMedian = n;
            check = true;
        }
    }
    // get standard deviation
    var theStandDev = 0;
    for (var o = 0; o < theHist.length; o++) {
        theStandDev = theStandDev + (Math.pow((o - theMean), 2) * theHist[o]);
    }
    theStandDev = Math.sqrt(theStandDev / thePixels);
    // Return the properties: theMean = [0] | theMedian = [1] | theStandDev = [2]
    return ([theMean, theMedian, theStandDev]);
}

 

  1. Copy the code text to the clipboard
  2. Open a new blank file in a plain-text editor (not in a word processor)
  3. Paste the code in
  4. Save as a plain text format file – .txt
  5. Rename the saved file extension from .txt to .jsx
  6. Install or browse to the .jsx file to run (see below)

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

 

leol30
leol30Author
Known Participant
October 11, 2023

@Stephen Marsh 

 

I changed the tolerance/threshold for neutral value to 0.00 and the script found the obvious coloured images and also gave me three more that I would have missed on visual inspection. Thanks very much for your support, JPEG and scripting knowledge. WOW! Thank you very much for the scriptMuch appreciated.

Stephen Marsh
Community Expert
Community Expert
October 11, 2023
quote

Are there any improvements or suggestions that would make things better?

 

If so, let me know... 


By @Stephen Marsh


Thanks for the offer. I'm really pleased with the generated text file list so that I can move the files myself but if it is possible to move the files to another folder that would be "the icing on the cake".


quoteThanks for the offer. I'm really pleased with the generated text file list so that I can move the files myself but if it is possible to move the files to another folder that would be "the icing on the cake".

By @leol30

 

Would you like the script to make a "Rejects" folder in the source folder and move the files that fail there?

 

(I was thinking of automatically opening the failed files in Photoshop)

Legend
October 10, 2023

This could probably be done using ImageJ instead of Adobe software.

https://imagej.net/

Stephen Marsh
Community Expert
Community Expert
October 9, 2023

@leol30 

 

I don’t hold much hope... 

 

I presume that these are neutral channel RGB grayscale images (R=G=B), not true grayscale mode.

 

The JPEG compression algorithm will likely create colour pixel artefacts which are not neutral.

 

In theory, it would be possible to create a script that would open the images in Photoshop and say convert to Lab colour mode, where it is easier to evaluate the histogram of the "a" and "b" channels for values which are not equal to zero, then apply a Bridge rating or label or other metadata to "mark" the file. Some sort of threshold value could be applied to filter our false positives that may contain a small amount of rogue coloured pixels... But all that is a lot of work!

 

It would be much easier to simply process an entire folder of images forcing them to be neutral, however, the JPEG format may create rogue pixels anyway!

 

You could upload a sample image that both meets and doesn't meet the criteria of being "coloured".

 

leol30
leol30Author
Known Participant
October 9, 2023

Stephen, thanks for your help. The images have different color profiles. I can remove grayscale images from the folder using the Bridge Color Mode filter. The method I use to find images that have coloured pixels is Open each image, turn up the Saturation to 100 and Save using a Photoshop action. I can then visually see the images that have colour pixels in them and move them to another folder. I thought there might be a "function" that could be used in a script to interrogate each file to see if any pixels were coloured rather than black, white or shades of grey. From what you've said I guess I'm asking for too much. Once again thanks for replying. I have uploaded four images so that you can see what I’m trying to explain. A picture is worth a thousand words! Two of images look black and white until the Saturation is turned up to 100.

 

 

gregreser
Legend
October 9, 2023

Inspecting a file for color pixels probably could be done, but it would be very difficult. It's beyond my knowledge.

An easier, but not 100% reliable, method would be to look at the metadata. This is not reliable because you have to count on prior software processes to correctly write color metadata and that doesn't always happen. Having said that, if your files have been processed that same way, then the metadata should be consistent, at least consistent enough to locate most of the color images. You could then manually test the remainder.

 

Here's one easy clue: your sample images are RGB

 

You can also see this in the Filter panel, but it shows the difficulty in relying on metadata.

rocks is sRGB

 

water is untagged (color images can also be untagged, unfortunately)

 

Have a look at the greyscale version of water.jpg and see what the Filter and File Properties say. Your file metadata might be consistent enough to identify the not-really-b&w images just using standard Bridge tools.