/* This macro creates a pseudo-height-map image from a horizontal line (interface) that can then be used for roughness measurement Uses embedded scale to set height values Assumes a horizontal interface (slope and curvature can be corrected by roughness software). Gaps in the interface will be ignored, the map will be reduced in width accordingly. It averages the interface values for reentrant features (i.e. where there are multiple “height” values for the same lateral location). v211121 1st version Peter J. Lee Florida State University v211129 1st version with options. v211130 Just adds a statistics output option and auto-orientation selection based on image aspect ratio. f5-f6: Updates stripKnownExtensionFromString function. F10: updated function unCleanLabel. */ macroL = "Create_Pseudo-Height_Map_From_Interface_w_Options_v211130-f10.ijm"; oTitle = getTitle; nTitle = stripKnownExtensionFromString(oTitle); getPixelSize(unit, pixelWidth, pixelHeight); medianBGIs = guessBGMedianIntensity(); medianBGI = round((medianBGIs[0]+medianBGIs[1]+medianBGIs[2])/3); getDimensions(oWidth, oHeight, oChannels, oSlices, oFrames); if (oWidth>oHeight) defSurf = "top"; else defSurf = "left"; Dialog.create("Options: Create Pseudo-Height Map From Interface"); Dialog.addMessage("Macro version: " + macroL); if(oChannels+oSlices+oFrames!=3) Dialog.addMessage("Warning: This macro has only been tested on single slice-frame-channel images",12,"red"); Dialog.addNumber("Background intensity",medianBGI,0,5,"defines what isn't interface"); if (pixelWidth!=pixelHeight) Dialog.addMessage("Warning: Pixels are not symmetrical, horizontal pixel size is used",12,"red"); Dialog.addRadioButtonGroup("Top surface is:",newArray("top","bottom","left","right"),1,4,defSurf); Dialog.addRadioButtonGroup("Surface depth view handling:",newArray("top \(stylus\)","average","bottom"),1,4,"top \(stylus\)"); Dialog.addNumber("Lines of repeated profile \(y\) created for pseudo 3D map",100,0,4,"pixels"); Dialog.addCheckbox("Print statistics?",false); Dialog.show(); bGI = Dialog.getNumber(); orientation = Dialog.getRadioButton(); viewType = Dialog.getRadioButton(); lineN = Dialog.getNumber(); printStats = Dialog.getCheckbox(); setBatchMode(true); if (orientation=="bottom") run("Rotate... ", "angle=180 grid=1 interpolation=None"); else if (orientation=="left") run("Rotate 90 Degrees Left"); else if (orientation=="right") run("Rotate 90 Degrees Right"); getDimensions(nWidth, nHeight, null, null, null); nTitle += "_Pseudo-"+viewType+"-HeightMap_" + unit; nTitle = unCleanLabel(nTitle); heightValues = newArray; for (i=0,l=0; i0){ if (startsWith(viewType,"top")) heightValues[l] = pixelWidth * hMin; if (startsWith(viewType,"ave")) heightValues[l] = pixelWidth * hSum/k; if (startsWith(viewType,"bot")) heightValues[l] = pixelWidth * hMax; l++; } } newImage("tempImage", "32-bit black", l, 1, 1); Array.getStatistics(heightValues, min, max, mean, stdDev); 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; } 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; } 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