Copy link to clipboard
Copied
This needs a creative mind.
I know we can sort pics via metadata in a bridge script, but is there any way to sort bright photos from dark photos? (regardless of aperture, flash, exposure time - based solely on illumination) I'd like to run a different action on each.
And if not in bridge, can anyone think of a clever conditional in photoshop actions?
Copy link to clipboard
Copied
Is there a different, consistent metadata value to help distinguish between a light and dark file?
If not, then this would likely need the help of Photoshop in order to assess the histogram.
Copy link to clipboard
Copied
I don't know if this will help. This script will create a csv file of mean histogram values for a folder of documents.
(Photoshop Script)
#target photoshop;
app.bringToFront();
main();
function main(){
var inputFolder= Folder.selectDialog ("Please select folder to process");
if(inputFolder == null) return;
var fileList = inputFolder.getFiles(/\.(jpg|dng|tif|psd|crw|cr2|psb|exr|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|rw2)$/i);
var outFile = File(Folder.desktop +"/"+decodeURI(inputFolder.name)+".csv");
outFile.open('w');
outFile.writeln("Filename,Mean Histogram");
for(var a in fileList){
open(fileList);
var area = activeDocument.histogram;
outFile.writeln(decodeURI(activeDocument.name)+ "," + mean(area).toFixed(0));
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
outFile.close();
alert("Process complete\nCSV created...\n" + decodeURI(outFile));
}
function mean(hist) {
var acc = 0;
var cnt = 0;
for (var i = 0; i < hist.length; i++) {
acc += i*hist;
cnt += hist;
}
return acc/cnt;
};
Copy link to clipboard
Copied
It does help SuperMerlin, I was looking into this using ImageJ as it can batch export out a spreadsheet file of the histogram mean value.
Is there any chance of the output having a column for the full file path+filename+extension?
Once we have an exported spreadsheet, then a Bride Script or ExifTool can be used to import a keyword, label, rating or other metadata entry in. Once there is metadata, it can be used to sort or filter… However I am still working on that bit, adding a conditional variable metadata entry based on the mean histogram value. Some sort of range would be required, such as:
Low Key = mean value between ???-???
Medium Key = mean value between ???-???
High Key = mean value between ???-???
At a start point, simply using the mean histogram value as a metadata value and then sorting on this value would be a start.
This is obviously not a one step workflow and there is still work to be done, but at least we have something brewing!
Copy link to clipboard
Copied
The script can be amended to add a label or metadata instead of outputing to a csv file.
It depends if the mean value will suffice?
Copy link to clipboard
Copied
Cool, that would help SuperMerlin, thanks!
I don’t know what else can be done? How do we tell software to differentiate between say low key and high key?
A perfectly “level” average/midtone key would = 128 mean levels
A perfect high key image would = 255 mean levels
A perfect low key image would = 0 mean levels
Of course, we are not dealing with perfectly flat gray/white/black images – there is a range of tones.
So it is probably too hard to try to work out a range, such as low key = 0-50 mean levels etc.
At least having a keyword appended to the image that was numerical would allow the sorting of low to high or high to low etc.
Would it be possible to do the following?
1) Append the mean value as a keyword (so as not to replace existing keywords)
2) Perhaps add a space and text to the value, so instead of say “66”, it would read as “66 mean histogram value” or something?
I’ll post what I have knocked up using your original script and ExifTool shortly.
Copy link to clipboard
Copied
OK, as a proof of concept, I used SuperMerlin’s original script to output the mean histogram level value to CSV file.
I then added the path to the image as a column. So the CSV import file had two columns with the following titles:
SourceFile,Subject
/path/to/file/hi.JPG,166 mean histogram value
/path/to/file/low.JPG,32 mean histogram value
/path/to/file/mid.jpg,128 mean histogram value
I then used the following ExifTool command to import and append the mean value as an entry in the Subject (keyword) metadata against each image:
exiftool -sep ',' -csv+='/path/to/csv/file/import-file.csv' 'path/to/image/folder'
Then in Bridge the keywords can be used to sort the images from say high key to low key based on the mean histogram value keyword:
Copy link to clipboard
Copied
Here is another version that labels the documents...
Big downfall at the moment. It does not do raw files.
#target photoshop;
app.bringToFront();
main();
function main(){
var inputFolder= Folder.selectDialog ("Please select folder to process");
if(inputFolder == null) return;
var fileList = inputFolder.getFiles(/\.(jpg|tif|psd|)$/i);
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
for(var a in fileList){
open(fileList);
var mean = meanHist(activeDocument.histogram).toFixed(0);
switch( true ){
case ( mean <= 110 😞 setLabel( fileList,"To Do"); break;
case ( mean > 111 && mean <= 180 😞 setLabel( fileList,"Approved"); break;
case ( mean >= 181 😞 setLabel( fileList,"Review"); break;
default : break;
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
};
function setLabel( file,label){
if ( !ExternalObject.AdobeXMPScript ) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
var xmpf = new XMPFile( File(file).fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_UPDATE );
var xmp = xmpf.getXMP();
xmp.deleteProperty(XMPConst.NS_XMP, "Label");
xmp.setProperty(XMPConst.NS_XMP, "Label",label);
if (xmpf.canPutXMP( xmp )) {
xmpf.putXMP( xmp );
}
xmpf.closeFile( XMPConst.CLOSE_UPDATE_SAFELY );
};
function meanHist(hist) {
var acc = 0;
var cnt = 0;
for (var i = 0; i < hist.length; i++) {
acc += i*hist;
cnt += hist;
}
return acc/cnt;
};
Copy link to clipboard
Copied
Nice one SuperMerlin!
I see that lines 14 , 15 and 16 control which mean histogram value range receives which label. The key will be adjusting the range of mean values to suit the range of images being processed and what the end user expects (I don’t that there are any empirical numerical standards that one can reference as to what mean RGB histogram values equate to say high key for example).
I presume that it is just a matter of swapping out the “setLabel” command for one that appends a keyword (although it is probably slightly harder than that)?
After sleeping on this, I realised that there are of course other ways instead of using the mean histogram value (whether RGB or Luminosity), however they would take more time. One method is using the Blur/Average filter and measuring the L value of Lab mode. Another method is resampling down to a 2x2 or even a 1 pixel image and again, perhaps using the L value of Lab mode as the metric as to how dark or light the image is.
As for raw camera images, it is sadly the nature of the beast. Raw camera data uses a linear gamma and the raw histogram is “biased” (I’m sure that it would still be workable and not impossible to deal with) – not to mention that they are “latent images” and it depends on the processing/development settings as to what the final histogram values are. Even camera manufacturers convert the raw data into a “default” rendered state to show the histogram “preview” on the back of the camera, it is not an exact science. This is why the camera histo may show highlight clipping, while it is still possible to recover 1 stop of data from the raw file that would be clipped on the rendered JPEG.
EDIT: If you could access the embedded thumbnail in the raw image or the Bridge preview files, then you could get to the rendered histo, otherwise you would need to open the raw file, apply a default preset to render, then open into Photoshop, run your script code etc. I don’t believe that Adobe allows one to access the raw file’s histogram, but I could be wrong. I can envisage other workflows where CLI tools such as DCRAW, EXIFTOOL and others may be used to do similar, piping the output of one into the other etc.
Copy link to clipboard
Copied
Yes the problem would be setting the values Stephen.
I have fixed the script so that it handles raw files as well...
#target photoshop;
app.bringToFront();
main();
function main(){
var inputFolder= Folder.selectDialog ("Please select folder to process");
if(inputFolder == null) return;
var fileList = inputFolder.getFiles(/\.(jpg|dng|tif|psd|crw|cr2|psb|exr|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|rw2)$/i);
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
for(var a in fileList){
open(fileList);
var EXT =fileList.name.toLowerCase().match(/[^\.]+$/).toString();
var mean = meanHist(activeDocument.histogram).toFixed(0);
if(!isRawFile(EXT)){
switch( true ){
case ( mean <= 110 😞 setLabel( fileList,"To Do"); break;
case ( mean > 111 && mean <= 180 😞 setLabel( fileList,"Approved"); break;
case ( mean >= 181 😞 setLabel( fileList,"Review"); break;
default : break;
}
}else{
//raw file
var file= File(decodeURI(fileList).replace(/\.[^\.]+$/, '.xmp'));
switch( true ){
case ( mean <= 110 😞 setMetadata( file,"To Do"); break;
case ( mean > 111 && mean <= 180 😞 setMetadata( file,"Approved"); break;
case ( mean >= 181 😞 setMetadata( file,"Review"); break;
default : break;
}
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
};
function setMetadata( file, label ){
try{
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
if(file.exists){
file.open('r');
file.encoding = "UTF8";
file.lineFeed = "unix";
file.open("r", "TEXT", "????");
var xmpStr = file.read();
file.close();
}else{
var xmpStr='';
}
var xmp = new XMPMeta( xmpStr );
xmp.deleteProperty(XMPConst.NS_XMP, "Label");
xmp.setProperty(XMPConst.NS_XMP, "Label",label);
file.open('w');
file.encoding = "UTF8";
file.lineFeed = "unix";
file.write( xmp.serialize() );
file.close();
}catch(e){alert(e+"-"+e.line);}
}
function isRawFile(ext){
var rawFileTypes = Array( "tiff", "crw", "nef", "raf", "orf", "mrw", "dcr", "mos", "srf", "pef", "dcr", "cr2", "erf", "x3f", "raw" );
for(var a in rawFileTypes){
if(ext.toString().toLowerCase() == rawFileTypes.toString()){
return true;
}
}
return false;
};
function setLabel( file,label){
if ( !ExternalObject.AdobeXMPScript ) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
var xmpf = new XMPFile( File(file).fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_UPDATE );
var xmp = xmpf.getXMP();
xmp.deleteProperty(XMPConst.NS_XMP, "Label");
xmp.setProperty(XMPConst.NS_XMP, "Label",label);
if (xmpf.canPutXMP( xmp )) {
xmpf.putXMP( xmp );
}
xmpf.closeFile( XMPConst.CLOSE_UPDATE_SAFELY );
};
function meanHist(hist) {
var acc = 0;
var cnt = 0;
for (var i = 0; i < hist.length; i++) {
acc += i*hist;
cnt += hist;
}
return acc/cnt;
};
Copy link to clipboard
Copied
Yet another version, with the code tidied and using Lab mode.
#target photoshop;
app.bringToFront();
main();
function main(){
var inputFolder= Folder.selectDialog ("Please select folder to process");
if(inputFolder == null) return;
var fileList = inputFolder.getFiles(/\.(jpg|dng|tif|psd|crw|cr2|psb|exr|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|rw2)$/i);
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
for(var a in fileList){
open(fileList);
var doc = activeDocument;
if (doc.mode != DocumentMode.LAB) doc.changeMode(ChangeMode.LAB);
var L = activeDocument.channels["Lightness"].histogram;
var mean = meanHist(L).toFixed(0);
var label = "";
switch( true ){
case ( mean <= 110 😞 label="To Do"; break;
case ( mean > 111 && mean <= 180 😞 label="Approved"; break;
case ( mean >= 181 😞 label="Review"; break;
default : break;
}
if(fileType() != "Camera Raw"){
setLabel( fileList, label);
}else{
//raw file
var file= File(decodeURI(fileList).replace(/\.[^\.]+$/, '.xmp'));
setMetadata( file,label);
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
};
function setMetadata( file, label ){
try{
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
if(file.exists){
file.open('r');
file.encoding = "UTF8";
file.lineFeed = "unix";
file.open("r", "TEXT", "????");
var xmpStr = file.read();
file.close();
}else{
var xmpStr='';
}
var xmp = new XMPMeta( xmpStr );
xmp.deleteProperty(XMPConst.NS_XMP, "Label");
xmp.setProperty(XMPConst.NS_XMP, "Label",label);
file.open('w');
file.encoding = "UTF8";
file.lineFeed = "unix";
file.write( xmp.serialize() );
file.close();
}catch(e){alert(e+"-"+e.line);}
}
function setLabel( file,label){
if ( !ExternalObject.AdobeXMPScript ) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
var xmpf = new XMPFile( File(file).fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_UPDATE );
var xmp = xmpf.getXMP();
xmp.deleteProperty(XMPConst.NS_XMP, "Label");
xmp.setProperty(XMPConst.NS_XMP, "Label",label);
if (xmpf.canPutXMP( xmp )) {
xmpf.putXMP( xmp );
}
xmpf.closeFile( XMPConst.CLOSE_UPDATE_SAFELY );
};
function meanHist(hist) {
var acc = 0;
var cnt = 0;
for (var i = 0; i < hist.length; i++) {
acc += i*hist;
cnt += hist;
}
return acc/cnt;
};
function fileType(){
var ref = new ActionReference();
ref.putProperty( charIDToTypeID("Prpr"), stringIDToTypeID("format"));
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
return executeActionGet(ref).getString(stringIDToTypeID("format"));
};
Copy link to clipboard
Copied
This is all fantastic. I am overwhelmed by the talent on this forum! I finally have a day off work tomorrow and will give it a trial run.
Copy link to clipboard
Copied
Not working in Photoshop CC2018 and CC2019 😞
I've got the error:
Error 1242: Illegal argument - argument 1
- File/Folder expected
Une: 19
-> open(fileList);
Copy link to clipboard
Copied
Same here, have you found the solution maybe?
Copy link to clipboard
Copied
This is an old thread and there is still no definition of light and dark. What about an image that is half black and half white?
You can use Blur->Average and then sample the image, that's the quickest way to get a composite value, but it doesn't tell you anything about the image.
Copy link to clipboard
Copied
@Lumigraphics I need my script to write out RGB mean/median values of a histogram into a CSV file (channels separated). The first post from Merlin seemed like it could be helpful, but I was getting the same message as @oski1941 :
Error 1242: Illegal argument - argument 1
- File/Folder expected
Une: 19
-> open(fileList);
Do you know why this is happening?
Copy link to clipboard
Copied
No idea, you'll need to look at the code to see how its supposed to work. People here try to help with solutions but this is not a "free programmers for all" forum and you may need to get your hands dirty to make things work.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
And also [i] in the mean histogram function as you found here:
Copy link to clipboard
Copied
Awesome script, thanksSuperMerlin I'm only working with jpgs and in lightroom, so I would like to avoid xmp. How can I write this directly to the exif instead of creating an xmp?
Also, can anyone thing of how I could sort images in lightroom this way? Without renaming the filename.
Copy link to clipboard
Copied
This version writes the mean value to keywords I.E. "Mean value = 114.34"
Sorry I know nothing about Lightroom, so can't help there.
#target photoshop;
app.bringToFront();
main();
function main(){
var inputFolder= Folder.selectDialog ("Please select folder to process");
if(inputFolder == null) return;
var fileList = inputFolder.getFiles(/\.(jpg|dng|tif|psd|psb|exr|png)$/i);
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
for(var a in fileList){
open(fileList);
var doc = activeDocument;
if (doc.mode != DocumentMode.LAB) doc.changeMode(ChangeMode.LAB);
var L = activeDocument.channels["Lightness"].histogram;
var mean = meanHist(L).toFixed(2);
if(fileType() != "Camera Raw"){
setMetadata( fileList, mean);
}else{ continue;}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
};
function setMetadata( file,mean){
var data = "Mean value = " + mean;
if ( !ExternalObject.AdobeXMPScript ) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');
var xmpf = new XMPFile( File(file).fsName, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_UPDATE );
var xmp = xmpf.getXMP();
xmp.appendArrayItem(XMPConst.NS_DC, "subject", data, 0,XMPConst.PROP_IS_ARRAY);
if (xmpf.canPutXMP( xmp )) {
xmpf.putXMP( xmp );
}
xmpf.closeFile( XMPConst.CLOSE_UPDATE_SAFELY );
};
function meanHist(hist) {
var acc = 0;
var cnt = 0;
for (var i = 0; i < hist.length; i++) {
acc += i*hist;
cnt += hist;
}
return acc/cnt;
};
function fileType(){
var ref = new ActionReference();
ref.putProperty( charIDToTypeID("Prpr"), stringIDToTypeID("format"));
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
return executeActionGet(ref).getString(stringIDToTypeID("format"));
};
Copy link to clipboard
Copied
Doesn't work for me too on PS 21.2. Seems everything related to activeDocument.histogram goes down a black hole on newer versions of PS. Any thoughts on that? Is that a known bug or a change in the API?
Copy link to clipboard
Copied
Awesome script, thanksSuperMerlin. I'm only working with jpgs and in lightroom, so I would like to avoid xmp. How can I write this directly to the exif instead of creating an xmp?
Also, can anyone thing of how I could sort images in lightroom this way? Without renaming the filename.
Jarrod, do you mean that if you are working on an image titled “myphoto_1234.jpg” that in the same location, a separate file will also be generated with the title “myphoto_1234.xmp”? An XMP “sidecar” file should only be created for raw files if your Bridge/ACR settings are configured for “XMP Sidecar” and not for “Camera Raw Database”. Metadata (such as labels, keywords etc) should be written to all rendered files, except raw which is where the sidecar may come into play if not using the CRDB.
Copy link to clipboard
Copied
I've tested/tried everyone script above and noone work. I've tried it on CC2018, CC2019 and older version of Photoshop - CS2.
I've got error:
Error 1242: Illegal argument - argument 1
- File/Folder expected
Une: 19
-> open(fileList);
So I changed open(fileList) to open(fileList[a]) and script startr running but nothing happen, only open and close files from selected folder.
Is anything I'm doing wrong? I run script directly from Photoshop -> Files -> Scripts. I have more then 5 milion photos day and night and I want separate day from nighy shots. Do this task manual is neverending story, so maybe someone could help me to run this script and help automate Label .jpg photos.
Copy link to clipboard
Copied
Have you thought of using the time captured in metadata? 9PM is night, 3PM is day. etc.