/* Simple macro to apply linear contrast stretches via separate RGB color channels using ImageJ's enhance contrast command v180709 1st version Peter J. Lee, NHMFL, Florida State University v180711 minor tweaks. v200428 added normalize. v210408 added options dialog and exclusions designed to help with black and white labels. v210414 Fixed bug that required expansion of a selection. v230330 Added use-histogram option. */ macroL = "Stretch_Channels_v230330"; setBatchMode(true); fullFName = getInfo("image.filename"); oImageID = getImageID(); saturation = 0; useSelArea = false; cropIt = false; stretchArea = "All"; if (fullFName=="") { fName = getTitle(); extension = "";} else { path = getInfo("image.directory"); fName = File.getNameWithoutExtension(path+fullFName); extension = substring(fullFName, lengthOf(fName)); } newTitle = fName + "+stretchCh" + extension; if (selectionType>=0){ selectionExists = true; /* In this version always uses selection if there is one */ run("Select None"); run("Restore Selection"); } else selectionExists = false; Dialog.create("Selection options"){ Dialog.addMessage("Macro version: "+macroL); if (selectionExists){ Dialog.addCheckbox("Use selected area to determine stretch", true); Dialog.addRadioButtonGroup("Stretch limited to:",newArray("Selected_region","Unselected_region","All"),3,1,"All"); Dialog.addCheckbox("Crop to selected region", false); } else { Dialog.addMessage("Use these settings to exclude back and white objects"); Dialog.addNumber("Exclude black \(-1 = No\)",-1,0,2,"intensity range, 0 = black only"); Dialog.addNumber("Exclude white \(-1 = No\)",-1,0,2,"intensity range, 0 = black only"); } Dialog.addNumber("Expand exclusion selection by",0,0,3,"pixels"); Dialog.addMessage("Stretch options:_____________"); Dialog.addCheckbox("Normalize channels?",false); Dialog.addCheckbox("Use stack histogram \(retains color balance\)?",false); Dialog.addNumber("Saturated pixels",saturation,1,3,"%"); Dialog.addString("New file name:",newTitle,newTitle.length+2); Dialog.addCheckbox("Tile image windows for comparison", false); Dialog.show; if (selectionExists){ useSelArea = Dialog.getCheckbox(); stretchArea = Dialog.getRadioButton(); cropIt = Dialog.getCheckbox(); } else { excludeBlack = Dialog.getNumber(); excludeWhite = Dialog.getNumber(); if (excludeBlack>=0 || excludeWhite>=0){ stretchArea = "Unselected_region"; maskW = "exclude2"; if (excludeBlack>=0 && excludeWhite>=0) combo = true; else { combo = false; maskW = "exclude"; } if (excludeBlack>=0){ run("Duplicate...", "title=exclude"); run("8-bit"); setThreshold(0, excludeBlack); setOption("BlackBackground", false); run("Convert to Mask"); if (is("Inverting LUT")) run("Invert LUT"); } if (excludeWhite>=0){ selectImage(oImageID); run("Duplicate...", "title=" + maskW); run("8-bit"); low = 255-excludeWhite; setThreshold(low, 255); setOption("BlackBackground", false); run("Convert to Mask"); if (is("Inverting LUT")) run("Invert LUT"); } if (excludeBlack>=0 && excludeWhite>=0){ imageCalculator("Max", "exclude","exclude2"); closeImageByTitle("exclude2"); } run("Create Selection"); setSelectionName("stretch_exclude"); run("Make Inverse"); selectionExists = true; run("Select None"); closeImageByTitle("exclude"); selectImage(oImageID); // run("Restore Selection"); } else stretchArea = "All"; } enlargeSel = Dialog.getNumber(); normalize = Dialog.getCheckbox(); useHist = Dialog.getCheckbox(); saturation = Dialog.getNumber(); newTitle = Dialog.getString(); tileWindows = Dialog.getCheckbox(); } if (selectionExists && stretchArea!="All"){ run("Restore Selection"); if (enlargeSel!=0) run("Enlarge...", "enlarge=" + enlargeSel); if (stretchArea=="Selected_region") run("Make Inverse"); run("Copy"); if (stretchArea=="Selected_region") run("Make Inverse"); } run("Select None"); run("Duplicate...", "title=temp"); run("RGB Stack"); enhSettings = "saturated=&saturation"; if (normalize) enhSettings += " normalize"; enhSettings += " process_all"; if (useHist) enhSettings += " use"; if (useSelArea) enhSettings += " update"; if (selectionType>=0){ if (selectionType>=0){ run("Restore Selection"); } } run("Enhance Contrast...",enhSettings); run("Stack to RGB"); rename(newTitle); closeImageByTitle("temp"); if (stretchArea!="All"){ run("Restore Selection"); if (stretchArea=="Selected_region") run("Make Inverse"); run("Paste"); if (stretchArea=="Selected_region") run("Make Inverse"); run("Select None"); run("Restore Selection"); } if (cropIt){ run("Restore Selection"); run("Crop"); run("Select None"); rename(newTitle + "_crop"); } setBatchMode("exit and display"); call("java.lang.System.gc"); /* force a garbage collection */ if (tileWindows) run("Tile"); showStatus("Stretch channels complete"); /* 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); }