macro "Fancy Border" { /* v190503 1st version PJL 5/3/2019 4:54 PM v190506 Adds ability to add all three of inner, outer and center borders. + v200706 Changed imageDepth variable name added macro label. + v211022 Updated color choices + v211025 Updated stripKnownExtensionFromString + v211104: Updated stripKnownExtensionFromString function 211112, 220616, 220816, 230505(f10) 060723: Again f5: updated pad function f6-9: updated colors. F12: Updated indexOf functions. F13: getColorArrayFromColorName_v230908. F17 : Replaced function: pad. F18: Updated getColorFromColorName function (012324). F19: updated function unCleanLabel. */ macroL = "Fancy_Border_v211112-f19.ijm"; requires("1.52i"); /* Utilizes Overlay.setPosition(0) from IJ >1.52i */ saveSettings; selEType = selectionType; /* Returns the selection type, where 0=rectangle, 1=oval, 2=polygon, 3=freehand, 4=traced, 5=straight line, 6=segmented line, 7=freehand line, 8=angle, 9=composite and 10=point.*/ if (selEType>=0) { getSelectionBounds(selEX, selEY, selEWidth, selEHeight); if ((selEWidth + selEHeight)<6) selEType=-1; /* Ignore junk selections that are suspiciously small */ } else exit("Sorry, a selection is required"); setBatchMode(true); originalImage = getTitle(); imageDepth = bitDepth(); getDimensions(imageWidth, imageHeight, channels, slices, frames); run("Create Mask"); if(is("Inverting LUT")) run("Invert LUT"); /* more effectively removes Inverting LUT */ rename("selection_mask"); run("Select None"); selectWindow(originalImage); startSliceNumber = getSliceNumber(); remSlices = slices-startSliceNumber; dBrderThick = maxOf(round((imageWidth+imageHeight)/1000),1); colorChoices = newArray("white", "black", "off-white", "off-black", "light_gray", "gray", "dark_gray"); colorChoicesStd = newArray("red", "green", "blue", "cyan", "magenta", "yellow", "pink", "orange", "violet"); colorChoicesMod = newArray("garnet", "gold", "aqua_modern", "blue_accent_modern", "blue_dark_modern", "blue_modern", "blue_honolulu", "gray_modern", "green_dark_modern", "green_modern", "green_modern_accent", "green_spring_accent", "orange_modern", "pink_modern", "purple_modern", "red_n_modern", "red_modern", "tan_modern", "violet_modern", "yellow_modern"); colorChoicesNeon = newArray("jazzberry_jam", "radical_red", "wild_watermelon", "outrageous_orange", "supernova_orange", "atomic_tangerine", "neon_carrot", "sunglow", "laser_lemon", "electric_lime", "screamin'_green", "magic_mint", "blizzard_blue", "dodger_blue", "shocking_pink", "razzle_dazzle_rose", "hot_magenta"); if (imageDepth==24) colorChoices = Array.concat(colorChoices,colorChoicesStd,colorChoicesMod,colorChoicesNeon); fancyBorderLocationsString = call("ij.Prefs.get", "fancy.borderLocations", "false|true|false"); fancyBorderLocations = split(fancyBorderLocationsString,"|"); Dialog.create("Border Format: " + macroL); Dialog.addMessage("Borders are added in the order: inner,outer,center") Dialog.addCheckbox("Draw border marking inside of selection?", fancyBorderLocations[0]); Dialog.addNumber("Width of inner border:",dBrderThick,0,3,"pixels"); iIBC = indexOfArray(colorChoices, call("ij.Prefs.get", "fancy.innerBorderColor",colorChoices[1]),1); Dialog.addChoice("Inner border color:", colorChoices, colorChoices[iIBC]); Dialog.addCheckbox("Draw border marking outside of selection?", fancyBorderLocations[1]); Dialog.addNumber("Width of outer border:",dBrderThick,0,3,"pixels"); iOBC = indexOfArray(colorChoices, call("ij.Prefs.get", "fancy.outerBorderColor",colorChoices[1]),1); Dialog.addChoice("Outer border color:", colorChoices, colorChoices[iOBC]); Dialog.addCheckbox("Draw border marking centered on selection?", fancyBorderLocations[2]); Dialog.addNumber("Width of center border:",dBrderThick,0,3,"pixels"); iCBC = indexOfArray(colorChoices, call("ij.Prefs.get", "fancy.centerBorderColor",colorChoices[1]),1); Dialog.addChoice("Center border color:", colorChoices, colorChoices[iCBC]); overwriteChoice = newArray("Add overlays"); if (Overlay.size>0) overwriteChoice = Array.concat(overwriteChoice, "Replace ALL overlays"); if (selEType<2) overwriteChoice = Array.concat(overwriteChoice, "Destructive overwrite", "New image"); if (overwriteChoice.length==1) Dialog.addMessage("Output:_________________ \n \n Borders added as overlays.\n Use rectangle or ellipse selections for\n destructive overwrite.\n Use 'flatten' to merge overlays with image."); else Dialog.addRadioButtonGroup("Output:_________________ ", overwriteChoice, 3, overwriteChoice.length, overwriteChoice[overwriteChoice.length-1]); Dialog.show(); innerBorder = Dialog.getCheckbox(); innerBorderThickness = Dialog.getNumber; /* set minimum default bar height as 2 pixels */ innerBorderColor = Dialog.getChoice; outerBorder = Dialog.getCheckbox(); outerBorderThickness = Dialog.getNumber; /* set minimum default bar height as 2 pixels */ outerBorderColor = Dialog.getChoice; centerBorder = Dialog.getCheckbox(); centerBorderThickness = Dialog.getNumber; /* set minimum default bar height as 2 pixels */ centerBorderColor = Dialog.getChoice; if (selEType<2 || Overlay.size>0) overWrite = Dialog.getRadioButton; else overWrite = "Add overlays"; if (startsWith(overWrite,"Replace")) while (Overlay.size!=0) Overlay.remove; fancyBorderLocations = "" + innerBorder + "|" + outerBorder + "|" + centerBorder; call("ij.Prefs.set", "fancy.borderLocations", fancyBorderLocations); /* save last used settings in user in preferences */ call("ij.Prefs.set", "fancy.innerBorderColor", innerBorderColor); innerBorderColorHex = getHexColorFromColorName(innerBorderColor); call("ij.Prefs.set", "fancy.outerBorderColor", outerBorderColor); outerBorderColorHex = getHexColorFromColorName(outerBorderColor); call("ij.Prefs.set", "fancy.centerBorderColor", centerBorderColor); centerBorderColorHex = getHexColorFromColorName(centerBorderColor); expSelI = -round(innerBorderThickness/2); expSelO = round(outerBorderThickness/2); /* If Overlay chosen add fancy border as overlay */ if (endsWith(overWrite,"verlays")){ run("Restore Selection"); if (innerBorder) { getSelectionFromMask("selection_mask"); run("Enlarge...", "enlarge=&expSelI pixel"); setSelectionName("Fancy inner border"); Overlay.addSelection(innerBorderColorHex, innerBorderThickness); } if (outerBorder) { getSelectionFromMask("selection_mask"); run("Enlarge...", "enlarge=&expSelO pixel"); setSelectionName("Fancy outer border"); Overlay.addSelection(outerBorderColorHex, outerBorderThickness); } if (centerBorder) { getSelectionFromMask("selection_mask"); setSelectionName("Fancy center border"); Overlay.addSelection(centerBorderColorHex, centerBorderThickness); } } else { if (startsWith(overWrite,"Destructive overwrite")) { tS = originalImage; } else { tS = "" + stripKnownExtensionFromString(unCleanLabel(originalImage)) + "+selBrdr"; run("Select None"); selectWindow(originalImage); run("Duplicate...", "title=&tS duplicate"); run("Restore Selection"); } selectWindow(tS); /* Tries to remove any old scale related overlays from copied image but usually leaves 2 ¯\_(?)_/¯ */ if(Overlay.size>0) { initialOverlaySize = Overlay.size; for (i=0; i=0) Overlay.removeSelection(j); } } } } if(selEType==0) { if (innerBorder) { setColor(innerBorderColorHex); setLineWidth(innerBorderThickness); drawRect(selEX-expSelI, selEY-expSelI, selEWidth+2*expSelI, selEHeight+2*expSelI); } if (outerBorder) { setColor(outerBorderColorHex); setLineWidth(outerBorderThickness); drawRect(selEX-expSelO, selEY-expSelO, selEWidth+2*expSelO, selEHeight+2*expSelO); } if (centerBorder) { setColor(centerBorderColorHex); setLineWidth(centerBorderThickness); drawRect(selEX, selEY, selEWidth, selEHeight); } } if(selEType==1) { if (innerBorder) { setColor(innerBorderColorHex); setLineWidth(innerBorderThickness); drawOval(selEX-expSelI, selEY-expSelI, selEWidth+2*expSelI, selEHeight+2*expSelI); } if (outerBorder) { setColor(outerBorderColorHex); setLineWidth(outerBorderThickness); drawOval(selEX-expSelO, selEY-expSelO, selEWidth+2*expSelO, selEHeight+2*expSelO); } if (centerBorder) { setColor(centerBorderColorHex); setLineWidth(centerBorderThickness); drawOval(selEX, selEY, selEWidth, selEHeight); } } } restoreSettings(); getSelectionFromMask("selection_mask"); closeImageByTitle("selection_mask"); setBatchMode("exit & display"); /* exit batch mode */ showStatus("Fancy Borders Added"); } /* ( 8(|) ( 8(|) 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 getColorArrayFromColorName(colorName) { /* v180828 added Fluorescent Colors v181017-8 added off-white and off-black for use in gif transparency and also added safe exit if no color match found v191211 added Cyan v211022 all names lower-case, all spaces to underscores v220225 Added more hash value comments as a reference v220706 restores missing magenta v230130 Added more descriptions and modified order. v230908: Returns "white" array if not match is found and logs issues without exiting. v240123: Removed duplicate entries: Now 53 unique colors */ functionL = "getColorArrayFromColorName_v240123"; cA = newArray(255,255,255); /* defaults to white */ if (colorName == "white") cA = newArray(255,255,255); else if (colorName == "black") cA = newArray(0,0,0); else if (colorName == "off-white") cA = newArray(245,245,245); else if (colorName == "off-black") cA = newArray(10,10,10); else if (colorName == "light_gray") cA = newArray(200,200,200); else if (colorName == "gray") cA = newArray(127,127,127); else if (colorName == "dark_gray") cA = newArray(51,51,51); else if (colorName == "red") cA = newArray(255,0,0); else if (colorName == "green") cA = newArray(0,255,0); /* #00FF00 AKA Lime green */ else if (colorName == "blue") cA = newArray(0,0,255); else if (colorName == "cyan") cA = newArray(0, 255, 255); else if (colorName == "yellow") cA = newArray(255,255,0); else if (colorName == "magenta") cA = newArray(255,0,255); /* #FF00FF */ else if (colorName == "pink") cA = newArray(255, 192, 203); else if (colorName == "violet") cA = newArray(127,0,255); else if (colorName == "orange") cA = newArray(255, 165, 0); else if (colorName == "garnet") cA = newArray(120,47,64); /* #782F40 */ else if (colorName == "gold") cA = newArray(206,184,136); /* #CEB888 */ else if (colorName == "aqua_modern") cA = newArray(75,172,198); /* #4bacc6 AKA "Viking" aqua */ else if (colorName == "blue_accent_modern") cA = newArray(79,129,189); /* #4f81bd */ else if (colorName == "blue_dark_modern") cA = newArray(31,73,125); /* #1F497D */ else if (colorName == "blue_honolulu") cA = newArray(0,118,182); /* Honolulu Blue #006db0 */ else if (colorName == "blue_modern") cA = newArray(58,93,174); /* #3a5dae */ else if (colorName == "gray_modern") cA = newArray(83,86,90); /* bright gray #53565A */ else if (colorName == "green_dark_modern") cA = newArray(121,133,65); /* Wasabi #798541 */ else if (colorName == "green_modern") cA = newArray(155,187,89); /* #9bbb59 AKA "Chelsea Cucumber" */ else if (colorName == "green_modern_accent") cA = newArray(214,228,187); /* #D6E4BB AKA "Gin" */ else if (colorName == "green_spring_accent") cA = newArray(0,255,102); /* #00FF66 AKA "Spring Green" */ else if (colorName == "orange_modern") cA = newArray(247,150,70); /* #f79646 tan hide, light orange */ else if (colorName == "pink_modern") cA = newArray(255,105,180); /* hot pink #ff69b4 */ else if (colorName == "purple_modern") cA = newArray(128,100,162); /* blue-magenta, purple paradise #8064A2 */ else if (colorName == "jazzberry_jam") cA = newArray(165,11,94); else if (colorName == "red_n_modern") cA = newArray(227,24,55); else if (colorName == "red_modern") cA = newArray(192,80,77); else if (colorName == "tan_modern") cA = newArray(238,236,225); else if (colorName == "violet_modern") cA = newArray(76,65,132); else if (colorName == "yellow_modern") cA = newArray(247,238,69); /* Fluorescent Colors https://www.w3schools.com/colors/colors_crayola.asp */ else if (colorName == "radical_red") cA = newArray(255,53,94); /* #FF355E */ else if (colorName == "wild_watermelon") cA = newArray(253,91,120); /* #FD5B78 */ else if (colorName == "shocking_pink") cA = newArray(255,110,255); /* #FF6EFF Ultra Pink */ else if (colorName == "razzle_dazzle_rose") cA = newArray(238,52,210); /* #EE34D2 */ else if (colorName == "hot_magenta") cA = newArray(255,0,204); /* #FF00CC AKA Purple Pizzazz */ else if (colorName == "outrageous_orange") cA = newArray(255,96,55); /* #FF6037 */ else if (colorName == "supernova_orange") cA = newArray(255,191,63); /* FFBF3F Supernova Neon Orange*/ else if (colorName == "sunglow") cA = newArray(255,204,51); /* #FFCC33 */ else if (colorName == "neon_carrot") cA = newArray(255,153,51); /* #FF9933 */ else if (colorName == "atomic_tangerine") cA = newArray(255,153,102); /* #FF9966 */ else if (colorName == "laser_lemon") cA = newArray(255,255,102); /* #FFFF66 "Unmellow Yellow" */ else if (colorName == "electric_lime") cA = newArray(204,255,0); /* #CCFF00 */ else if (colorName == "screamin'_green") cA = newArray(102,255,102); /* #66FF66 */ else if (colorName == "magic_mint") cA = newArray(170,240,209); /* #AAF0D1 */ else if (colorName == "blizzard_blue") cA = newArray(80,191,230); /* #50BFE6 Malibu */ else if (colorName == "dodger_blue") cA = newArray(9,159,255); /* #099FFF Dodger Neon Blue */ else IJ.log(colorName + " not found in " + functionL + ": Color defaulted to white"); return cA; } function setBackgroundFromColorName(colorName) { colorArray = getColorArrayFromColorName(colorName); setBackgroundColor(colorArray[0], colorArray[1], colorArray[2]); } /* Hex conversion below adapted from T.Ferreira, 20010.01 https://imagej.net/doku.php?id=macro:rgbtohex */ function getHexColorFromColorName(colorNameString) { /* v231207: Uses IJ String.pad instead of function: pad */ colorArray = getColorArrayFromColorName(colorNameString); r = toHex(colorArray[0]); g = toHex(colorArray[1]); b = toHex(colorArray[2]); hexName= "#" + "" + String.pad(r, 2) + "" + String.pad(g, 2) + "" + String.pad(b, 2); return hexName; } function getSelectionFromMask(sel_M){ batchMode = is("Batch Mode"); /* Store batch status mode before toggling */ if (!batchMode) setBatchMode(true); /* Toggle batch mode on if previously off */ tempTitle = getTitle(); selectWindow(sel_M); run("Create Selection"); /* Selection inverted perhaps because the mask has an inverted LUT? */ run("Make Inverse"); selectWindow(tempTitle); run("Restore Selection"); if (!batchMode) setBatchMode(false); /* Return to original batch mode setting */ } function indexOfArray(array, value, default) { /* v190423 Adds "default" parameter (use -1 for backwards compatibility). Returns only first found value v230902 Limits default value to array size */ index = minOf(lengthOf(array) - 1, default); for (i=0; i0){ 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; } function unCleanLabel(string) { /* v161104 This function replaces special characters with standard characters for file system compatible filenames. + 041117b to remove spaces as well. + v220126 added getInfo("micrometer.abbreviation"). + v220128 add loops that allow removal of multiple duplication. + v220131 fixed so that suffix cleanup works even if extensions are included. + v220616 Minor index range fix that does not seem to have an impact if macro is working as planned. v220715 added 8-bit to unwanted dupes. v220812 minor changes to micron and Ångström handling + v231005 Replaced superscript abbreviations that did not work. + v240124 Replace _+_ with +. */ /* Remove bad characters */ string = string.replace(fromCharCode(178), "sup2"); /* superscript 2 */ string = string.replace(fromCharCode(179), "sup3"); /* superscript 3 UTF-16 (decimal) */ string = string.replace(fromCharCode(0xFE63) + fromCharCode(185), "sup-1"); /* Small hyphen substituted for superscript minus as 0x207B does not display in table */ string = string.replace(fromCharCode(0xFE63) + fromCharCode(178), "sup-2"); /* Small hyphen substituted for superscript minus as 0x207B does not display in table */ string = string.replace(fromCharCode(181) + "m", "um"); /* micron units */ string = string.replace(getInfo("micrometer.abbreviation"), "um"); /* micron units */ string = string.replace(fromCharCode(197), "Angstrom"); /* Ångström unit symbol */ string = string.replace(fromCharCode(0x212B), "Angstrom"); /* the other Ångström unit symbol */ string = string.replace(fromCharCode(0x2009) + fromCharCode(0x00B0), "deg"); /* replace thin spaces degrees combination */ string = string.replace(fromCharCode(0x2009), "_"); /* Replace thin spaces */ string = string.replace("%", "pc"); /* % causes issues with html listing */ string = string.replace(" ", "_"); /* Replace spaces - these can be a problem with image combination */ /* Remove duplicate strings */ unwantedDupes = newArray("8bit", "8-bit", "lzw"); for (i=0; i=0) { string = string.substring(0, iFirst) + string.substring(string, iFirst + lengthOf(unwantedDbls[i]) / 2); i = -1; /* check again */ } } string = string.replace("_\\+", "\\+"); /* Clean up autofilenames */ string = string.replace("\\+_", "\\+"); /* Clean up autofilenames */ /* cleanup suffixes */ unwantedSuffixes = newArray(" ", "_", "-", "\\+"); /* things you don't wasn't to end a filename with */ extStart = lastIndexOf(string, "."); sL = lengthOf(string); if (sL-extStart<=4 && extStart>0) extIncl = true; else extIncl = false; if (extIncl){ preString = substring(string, 0, extStart); extString = substring(string, extStart); } else { preString = string; extString = ""; } for (i=0; i