/* Almost-better-than-nothing autoGamma correction based on an iterative adjustment to gamma until the mean intensity of each channel is gray (i.e. a sort of Gray World auto white balance based on gamma corrections. v180710 First version: Peter J. Lee, NHMFL, Florida State University v200414 Just adds strip extension function v200416 Added Use Selection option and applied to all image types v200420 Now works on gray stacks v200427 Added normalization v210406 Took normalization back out and added option to restrict rebalance to selected area v210719 Added slices parameter and removed options that were not working v211025 Updated functions v211104 Updated stripKnownExtensionFromString function v211112: Again v211216 Formula-based auto-gamma v220110 removed unused range. v230324 Added 0.5—5 constraints to gamma values. Updated stripKnownExtensionFromString function: 230505, 060723 */ macroL = "AutoGamma_v230324-f2.ijm"; originalBitDepth = bitDepth; imageID = getImageID(); originalTitle = stripKnownExtensionFromString(getTitle()); agammab_Title = originalTitle + "_aGamma"; getDimensions(width, height, channels, slices, frames); grayscale = is("grayscale"); if(slices>1 && !grayscale) exit("Sorry, this macro does not work with color stacks yet"); selUse = "Do not use selection"; selUses = newArray(selUse,"Use selection to determine correction for entire image", "Apply to selection only"); if (selectionType>=0) selUse = selUses[1]; getStatistics(null,meanIO, minIO, maxIO); rangeIO = maxIO-minIO; if (originalBitDepth==16) fMaxI = pow(2,16); else if (originalBitDepth==32) fMaxI = maxIO; else fMaxI = 255; stretchIOptions = newArray("Normalize \(stretch intensities across full bit depth\)","Equalize \(overrides clipping and normalize settings\)","High-low intensity clipping","High-low intensity clipping \(ignore outliers using % saturation below\)"); Dialog.create(macroL + " options"); if (selUse!=selUses[0]) Dialog.addRadioButtonGroup("Selection use:",selUses,3,1,selUse); Dialog.addMessage("Pre-gamma stretch channel\(s\) options:_________________"); Dialog.addRadioButtonGroup("Stretch intensities:",stretchIOptions,4,1,stretchIOptions[2]); Dialog.addNumber("High-low intensity clipping to ignore outliers: ",0.1,1,4,"%"); /* imageJ documentation suggests >0 to avoid outliers having undue influence */ Dialog.addMessage("Original intensity range \(" + rangeIO + "\): min = " + minIO + ", max = " + maxIO + ", mean = " + meanIO); Dialog.addCheckbox("Output processing details in log window",false); Dialog.show(); if (selUse!=selUses[0]) selUse = Dialog.getRadioButton(); stretchIOption = Dialog.getRadioButton(); norm = false; equal = false; clipI = false; clipIC = Dialog.getNumber(); if (stretchIOption==stretchIOptions[0]) norm = true; else if (stretchIOption==stretchIOptions[1]) equal = true; else clipI = true; if (stretchIOption==stretchIOptions[2]) clipIC = 0; outputProcessLog = Dialog.getCheckbox(); setBatchMode(true); eCOptions = " "; if (clipI) eCOptions += "saturated=" + clipIC; else if (norm) eCOptions += "normalize"; else if (equal) eCOptions += "equalize"; eCOptions += " update"; run("Select None"); if (slices==1) run("Duplicate...", "use"); else run("Duplicate...", "duplicate"); rename("temp"); fMaxI = pow(2,16); if (originalBitDepth==24 || !grayscale) { run("RGB Stack"); run("16-bit"); /* to minimize detail loss during iterative process below */ eCOptions += " process_all"; print("Enhance Contrast...", eCOptions); run("Enhance Contrast...", eCOptions); for (i=0; i<3; i++) { setSlice(i+1); if(!startsWith(selUse,"Do")) run("Restore Selection"); getStatistics(null,meanI, minI, maxI); gParameters = "value=" + Math.constrain(log(0.5)/log(meanI/fMaxI), 0.5, 5); if(!startsWith(selUse,"Apply")) run("Select None"); run("Gamma...", gParameters); getStatistics(null,meanIF, minIF, maxIF); // rangeIF = maxIF-minIF; } if (originalBitDepth==24) run("8-bit"); run("Stack to RGB"); } else if (originalBitDepth==8 || originalBitDepth==16) { if (originalBitDepth==8) run("16-bit"); if (slices>1) eCOptions += " process_all"; run("Select None"); run("Enhance Contrast...", eCOptions); for(i=1; i=0) { selectImage(imageID); run("Restore Selection"); selectWindow(agammab_Title); } setBatchMode("exit and display"); if (outputProcessLog) print("AutoGamma macro completed: Mean Intensity now at " + (100/max)*meanIntensity + "% of overall range, originally at " + (100/maxIO)*meanIO + "%"); showStatus("AutoGamma macro completed: Mean Intensity now at " + (100/max)*meanIntensity + "% of overall range"); /* End of AutoGamma macro */ /* ( 8(|) ( 8(|) All ASC Functions @@@@@:-) @@@@@:-) */ function closeImageByTitle(windowTitle) { /* Cannot be used with tables */ /* v181002 reselects original image at end if open v200925 uses "while" instead of if so it can also remove duplicates */ oIID = getImageID(); while (isOpen(windowTitle)) { selectWindow(windowTitle); close(); } if (isOpen(oIID)) selectImage(oIID); } function stripKnownExtensionFromString(string) { /* Note: Do not use on path as it may change the directory names v210924: Tries to make sure string stays as string. v211014: Adds some additional cleanup. v211025: fixes multiple 'known's issue. v211101: Added ".Ext_" removal. v211104: Restricts cleanup to end of string to reduce risk of corrupting path. v211112: Tries to fix trapped extension before channel listing. Adds xlsx extension. v220615: Tries to fix the fix for the trapped extensions ... v230504: Protects directory path if included in string. Only removes doubled spaces and lines. v230505: Unwanted dupes replaced by unusefulCombos. v230607: Quick fix for infinite loop on one of while statements. v230614: Added AVI. v230905: Better fix for infinite loop. v230914: Added BMP and "_transp" and rearranged */ fS = File.separator; string = "" + string; protectedPathEnd = lastIndexOf(string,fS)+1; if (protectedPathEnd>0){ protectedPath = substring(string,0,protectedPathEnd); string = substring(string,protectedPathEnd); } unusefulCombos = newArray("-", "_"," "); for (i=0; i=0) string = replace(string,combo,unusefulCombos[i]); } } if (lastIndexOf(string, ".")>0 || lastIndexOf(string, "_lzw")>0) { knownExts = newArray(".avi", ".csv", ".bmp", ".dsx", ".gif", ".jpg", ".jpeg", ".jp2", ".png", ".tif", ".txt", ".xlsx"); knownExts = Array.concat(knownExts,knownExts,"_transp","_lzw"); kEL = knownExts.length; for (i=0; i0){ preChan = substring(string,0,iChanLabels); postChan = substring(string,iChanLabels); while (indexOf(preChan,knownExts[i])>0){ preChan = replace(preChan,knownExts[i],""); string = preChan + postChan; } } } while (endsWith(string,knownExts[i])) string = "" + substring(string, 0, lastIndexOf(string, knownExts[i])); } } unwantedSuffixes = newArray(" ", "_","-"); for (i=0; i0){ if(!endsWith(protectedPath,fS)) protectedPath += fS; string = protectedPath + string; } return string; }