macro "Rotate Image to Line" { /* "Rotate Image to Line" This macro rotates an image based on the angle of a straight line selection. The image is rotated so objects oriented similarly to the line selection become horizontal. Original version from ImageJ example collection. v220111: Updated for new macro functions and added line drawing option PJL. v220315: Just updates the display (important for composite images and stacks) and adds beeps. v230214: Added log output, autocropping, and sets background to black or white, whichever is closest to medium intensity. v230216: Renames rotated (and cropped if cropped) title. Cropping is now optional and allows adjustment. v230404: Hides image while rotating as an indicator of progress. Adds rotation to title before manual crop because I sometimes forget to use the dialog to crop. v230410: Skips crop attempt if there is no selection. v230523: Adds options for interpolation and background. v230908: Maintains orientation. F2 : Replaced function: pad. F3: Updated getColorFromColorName function (012324). v240207: MOre precise wording in dialog. */ macroL = "Rotate_Image_To_Line_v240207_f1.ijm"; // requires("1.53f"); saveSettings(); /* To restore settings at the end */ getLine(x1, y1, x2, y2, lineWidth); if (x1==-1){ setTool("line"); waitForUser("Draw reference line for rotation and click on 'OK' when finished"); getLine(x1, y1, x2, y2, lineWidth); if (x1==-1) exit("Sorry, this macro needs a reference line"); } orTitle = "" + getTitleWOKnownExtension(); angle = Math.toDegrees(atan2(y1-y2, x2-x1)); if (angle>45 && angle<135) angle -= 90; else if (angle>225 && angle<315) angle -= 270; medianBGIs = guessBGMedianIntensity(); medianBGI = round((medianBGIs[0]+medianBGIs[1]+medianBGIs[2])/3); originalBGCol = Color.background; imageDepth = bitDepth(); degreeChar = fromCharCode(0x00B0); if (medianBGI>127) defBG = "white"; else defBG = "black"; Dialog.create("Rotation options: " + macroL); Dialog.setInsets(0, 0, -15); Dialog.addNumber("Rotation angle:",angle,4,6,degreeChar); scalingOptions = newArray("None","Bilinear","Bicubic"); Dialog.addRadioButtonGroup("Interpolation:",scalingOptions,1,3,"Bicubic"); options1 = newArray("Apply to copy of image", "Automatically expand canvas to accommodate rotation?", "Fill resulting empty space with color listed below?"); options1Checks = newArray(1,1,1); Dialog.addCheckboxGroup(3,1, options1, options1Checks); grayChoices = newArray("white", "black", "off-white", "off-black", "lightGray", "gray", "darkGray"); colorChoicesStd = newArray("red", "green", "blue", "cyan", "magenta", "yellow", "pink", "orange", "violet"); colorChoicesMaterials = newArray("bronze", "antique_bronze", "brass", "dull_brass", "chrome", "copper", "aged_copper", "dusky_copper", "light_copper", "garnet", "burnished_gold", "gold", "slate_gray", "titanium", "vault_garnet", "plaza_brick", "vault_gold"); colorChoicesMod = newArray("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"); colorChoices = Array.concat(grayChoices, colorChoicesStd, colorChoicesMaterials, colorChoicesMod, colorChoicesNeon); if (imageDepth==24) Dialog.addChoice("Color for background fill \(median BGI = "+medianBGI+"\):", colorChoices, defBG); else Dialog.addChoice("Gray tone of background:", grayChoices, defBG); Dialog.setInsets(0, 0, 5); Dialog.addCheckbox("Manually crop after rotation?",true); Dialog.addNumber("Enlarge auto-selection by buffer value:",0,0,3,"pixels"); Dialog.show(); angle = Dialog.getNumber(); interp = Dialog.getRadioButton(); makeCopy = Dialog.getCheckbox(); enlargeRot = Dialog.getCheckbox(); fillBG = Dialog.getCheckbox(); bgFillCol = Dialog.getChoice(); manCrop = Dialog.getCheckbox(); bufferPix = round(Dialog.getNumber()); setBatchMode(true); setBackgroundFromColorName(bgFillCol); newTitle = orTitle + "_rot"+d2s(angle,1); if (makeCopy) run("Duplicate...", "title=&newTitle ignore"); else rename(newTitle); showStatus("Rotating image by " + angle + " degrees \(" + macroL + "\)"); rotSettings = "angle=&angle grid=0 interpolation=&interp"; if (fillBG) rotSettings += " fill"; if (enlargeRot) rotSettings += " enlarge"; run("Rotate... ",rotSettings); orImageWidth = Image.width; orImageHeight = Image.height; setBatchMode("exit and display"); if (manCrop){ run("Select Bounding Box (guess background color)"); if (bufferPix!=0) run("Enlarge...", "enlarge=&bufferPix pixel"); waitForUser("Adjust selection box and click on 'OK' when finished"); if (getValue("selection.size")>0) run("Crop"); if(orImageWidth!=Image.width || orImageHeight!=Image.height) rename(newTitle + "_Crop"); } Color.setBackground(originalBGCol); beep();beep();beep(); IJ.log("Rotation to angle of line: "+angle+degreeChar+" completed"); setTool("rectangle"); /* Assumes that next task is to crop. */ updateDisplay(); call("java.lang.System.gc"); showStatus("Rotate to angle of line: "+angle+degreeChar+ " completed"); } /* ( 8(|)) ( 8(|)) ( 8(|)) ASC Functions @@@@@:-) @@@@@:-) @@@@@:-) */ function getTitleWOKnownExtension() { /* v230904: 1st version PJL Applied Superconductivity Center v230908: Simplified to replace only */ newTitle = getTitle(); lcExtns = newArray(".avi", ".csv", ".dsx", ".gif", ".jpeg", ".jpg", ".jp2", "_lzw.", ".ols", ".png", ".tiff", ".tif", ".txt", ".vsi", ".xlsx", ".xls"); /* Note: Always list 4 character extensions before 3 character extensions */ nExtns = lcExtns.length; for (i=0; i0; i++){ newTitle = replace(newTitle, lcExtns[i], ""); newTitle = replace(newTitle, toUpperCase(lcExtns[i]), ""); } return newTitle; } function guessBGMedianIntensity(){ /* v220822 1st color array version (based on https://wsr.imagej.net//macros/tools/ColorPickerTool.txt) */ iW = Image.width-1; iH = Image.height-1; interrogate = round(maxOf(1,(iW+iH)/200)); if (bitDepth==24){red = 0; green = 0; blue = 0;} else int = 0; xC = newArray(0,iW,0,iW); yC = newArray(0,0,iH,iH); xAdd = newArray(1,-1,1,-1); yAdd = newArray(1,1,-1,-1); if (bitDepth==24){ reds = newArray(); greens = newArray(); blues = newArray();} else ints = newArray; for (i=0; i>16)&0xff); // extract red byte (bits 23-17) greens = Array.concat(greens,(v>>8)&0xff); // extract green byte (bits 15-8) blues = Array.concat(blues,v&0xff); // extract blue byte (bits 7-0) } else ints = Array.concat(ints,getValue(xC[i]+j*xAdd[i],yC[i]+j*yAdd[i])); } } midV = round((xC.length-1)/2); if (bitDepth==24){ reds = Array.sort(reds); greens = Array.sort(greens); blues = Array.sort(blues); medianVals = newArray(reds[midV],greens[midV],blues[midV]); } else{ ints = Array.sort(ints); medianVals = newArray(ints[midV],ints[midV],ints[midV]); } return medianVals; } /* BAR color function mods */ 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. v240709: Added 2024 FSU-Branding Colors. Some reorganization. Now 60 unique colors. v260202: Added 12 (mostly metallic) "Materials" colors. Now 72 unique colors. */ functionL = "getColorArrayFromColorName_v260203"; 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 == "lightGray") cA = newArray(192, 192, 192); else if (colorName == "gray") cA = newArray(127, 127, 127); else if (colorName == "darkGray") cA = newArray(64, 64, 64); 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); /* Excel Modern + */ 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 == "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); /* FSU */ else if (colorName == "garnet") cA = newArray(120, 47, 64); /* #782F40 */ else if (colorName == "gold") cA = newArray(206, 184, 136); /* #CEB888 */ else if (colorName == "gulf_sands") cA = newArray(223, 209, 167); /* #DFD1A7 */ else if (colorName == "stadium_night") cA = newArray(16, 24, 32); /* #101820 */ else if (colorName == "westcott_water") cA = newArray(92, 184, 178); /* #5CB8B2 */ else if (colorName == "vault_garnet") cA = newArray(166, 25, 46); /* #A6192E */ else if (colorName == "legacy_blue") cA = newArray(66, 85, 99); /* #425563 */ else if (colorName == "plaza_brick") cA = newArray(66, 85, 99); /* #572932 */ else if (colorName == "vault_gold") cA = newArray(255, 199, 44); /* #FFC72C */ /* Materials */ else if (colorName == "bronze") cA = newArray(205, 127, 50); /* #CD7F32 */ else if (colorName == "antique_bronze") cA = newArray(102, 93, 30); /* #665D1E */ else if (colorName == "brass") cA = newArray(181, 166, 66); /* #B5A642 */ else if (colorName == "dull_brass") cA = newArray(142, 124, 80); /* #8E7C50 */ else if (colorName == "burnished_gold") cA = newArray(133, 109, 77); /* #856D4D */ else if (colorName == "chrome") cA = newArray(229, 228, 226); /* #E5E4E2 */ else if (colorName == "copper") cA = newArray(184, 115, 51); /* #B87333 */ else if (colorName == "aged_copper") cA = newArray(110, 58, 7); /* #6E3A07 */ else if (colorName == "dusky_copper") cA = newArray(110, 59, 59); /* #6E3B3B */ else if (colorName == "light_copper") cA = newArray(218, 138, 103); /* #DA8A67 */ else if (colorName == "slate_gray") cA = newArray(112, 128, 144); /* #708090 */ else if (colorName == "titanium") cA = newArray(135, 134, 129); /* #878681 */ /* Fluorescent Colors https://www.w3schools.com/colors/colors_crayola.asp */ else if (colorName == "radical_red") cA = newArray(255, 53, 94); /* #FF355E */ else if (colorName == "jazzberry_jam") cA = newArray(165, 11, 94); 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; } /* 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 setBackgroundFromColorName(colorName) { colorArray = getColorArrayFromColorName(colorName); setBackgroundColor(colorArray[0], colorArray[1], colorArray[2]); }