Supplementary File 1 for: aNMJ-morph - A simple macro for rapid analysis of neuromuscular junction (NMJ) morphology Royal Society Open Science Minty et al, 2020 Gavin Minty † Alex Hoppen † Ines Boehm † Abrar Alhindi Larissa Gibb Ellie Potter Boris C. Wagner Janice Miller Richard J. E. Skipworth Thomas H. Gillingwater Ross A. Jones * __________________________________________________________________________________________ Supplementary File 1 - aNMJ-morph macro text Full IJM-text (ImageJ Macro Language) transcription __________________________________________________________________________________________ // Configuration parameters to make the resulting Excel spreadsheet work with German localisation columnSeparator = ','; /* ',' by default, ';' if run on German machines */ decimalSeparator = '.'; /* '.' by default, ',' if run on German machines */ // Set this variable to true to print intermediate results to the Log printIntermediateResults = false; decimalPlaces = 8; function formatNumber(number) { return replace(d2s(number, 8), '.', decimalSeparator); } function printIfEnabled(message) { if (printIntermediateResults) { print(message); } } function safeSelectWindow(windowTitle) { // Running selectWindow while the layout engine is in progress may result in // undefined behaviour. Waiting for a short period resolves the issue. // 100ms seems to be enough delay while not slowing down macro execution too // much. wait(100); selectWindow(windowTitle); } function safeWaitForUser(message) { // Store the current window title, display a message to the user and restore // focus to that image. currentImageId = getImageID(); waitForUser(message); // Don't focus the old window if none was selected. wait(100); selectImage(currentImageId); } // Install Paintbrush Tool Options & other StartupMacros - // FIJI has a bug where it kicks out all StartupMacros when installing a Macro macrosDirectory = getDirectory("macros"); run("Install...", "install="+macrosDirectory+File.separator+"StartupMacros.fiji.ijm"); list = getList("image.titles"); if (list.length == 0) { // If no image is open, Go into batch mode. Ask for a directory directory = getDirectory("Select directory with images to process"); batchProcess(directory); } else { // Process the currently open image processOpenImage(list[0]); } function processOpenImage(fileName) { originalTitle = getTitle(); originalDirectory = getDirectory("image"); destination = originalDirectory + File.separator + "cleaned_images" + File.separator; axonFilename = "axon_terminal" + getTitle(); axonFilename = substring(axonFilename, 0, lastIndexOf(axonFilename, ".")) + ".tif"; axonFilepath = destination + axonFilename; endplateFilename = "muscle_endplate" + getTitle(); endplateFilename = substring(endplateFilename, 0, lastIndexOf(endplateFilename, ".")) + ".tif"; endplateFilepath = destination + endplateFilename; endplateIntermediateFilename = "muscle_intermediate_endplate" + getTitle(); endplateIntermediateFilename = substring(endplateIntermediateFilename, 0, lastIndexOf(endplateIntermediateFilename, ".")) + ".tif"; endplateIntermediateFilepath = destination + endplateIntermediateFilename; // In case images are taken as hyperstacks, a Maximum Intensity Projection is created if (is ("hyperstack")) { run("Z Project...", "projection=[Max Intensity]"); fileName = originalDirectory +File.separator+ getTitle(); saveAs("tiff", fileName); MAXtitle = getTitle(); safeSelectWindow(originalTitle); close(); safeSelectWindow(MAXtitle); originalTitle = getTitle(); } fileName = originalDirectory +File.separator+ originalTitle; File.makeDirectory(destination); outputFilename = originalDirectory + "raw_data_table.csv"; printIfEnabled(getTitle()); run("Clear Results"); run("Paintbrush Tool Options...", "brush=100"); if (getHeight() != getWidth()) { safeWaitForUser("WARNING: The image you are processing is not square. The results may not be accurate!"); } numberPixels = getHeight(); printIfEnabled("Number of pixels: " + numberPixels); getPixelSize(sizeUnit, pixelSizeX, pixelSizeY); if (pixelSizeX != pixelSizeY) { safeWaitForUser("WARNING: The image has a different scale on the X and Y axes. The results may not be accurate!"); } size = pixelSizeX * getWidth(); printIfEnabled("Image size: " + size + sizeUnit); getDimensions(width, height, numberOfChannels, slices, frames); Dialog.create("Welcome"); Dialog.addMessage("Welcome to 'aNMJ-morph' macro. \nSelect channel 1 that is currently displayed (either muscle endplate or nerve terminal). \nSelect channel 2 (either muscle endplate or nerve terminal). \n-\nPress OK."); for (i = 1; i <= numberOfChannels; i++) { Dialog.addChoice("Channel " + i, newArray("Ignore", "Muscle endplate", "Nerve terminal")); } Dialog.show(); muscleEndplateChannel = -1; nerveTerminalChannel = -1; for (i = 1; i <= numberOfChannels; i++) { choice = Dialog.getChoice(); if (choice == "Muscle endplate") { if (muscleEndplateChannel != -1) { exit("Error: More than one channel was selected as the muscle endplate channel"); } muscleEndplateChannel = i; } if (choice == "Nerve terminal") { if (nerveTerminalChannel != -1) { exit("Error: More than one channel was selected as the nerve terminal channel"); } nerveTerminalChannel = i; } } if (muscleEndplateChannel == -1) { exit("Error: No muscle endplate channel was selected"); } if (nerveTerminalChannel == -1) { exit("Error: No nerve terminal channel was selected"); } run("Arrange Channels...", "new=" + muscleEndplateChannel + '' + nerveTerminalChannel); Stack.setChannel(1); run("Split Channels"); run("Threshold..."); // Open second image as a Template for thresholding open(fileName); run("Arrange Channels...", "new=" + muscleEndplateChannel + '' + nerveTerminalChannel); Stack.setChannel(1); templateFileName = fileName + "_template.tif"; saveAs("tiff",templateFileName); run("Split Channels"); setTool(19); setForegroundColor(0, 0, 0); // Adjust position of windows safeSelectWindow("C2-" + originalTitle); getLocationAndSize(x, y, width, height); setLocation(50, 50); safeSelectWindow("C2-" + File.getName(templateFileName)); getLocationAndSize(x2, y2, width2, height2); setLocation(100 + width, 50); safeSelectWindow("C2-" + originalTitle); safeWaitForUser("2/7 Threshold Nerve terminal. \nUse the reference image as a guide to selecting the appropriate threshold. \nSelect a pre-set threshold from the drop down menu or adjust manually (top scroll bar). \n-\nHint: If you make any mistakes start macro again ('esc' exits macro). \n-\nErase Background Noise. \nPaintbrush tool is already selected. \nBrush width can be adjusted by double-clicking on brush icon. \n-\nPress OK."); thresholdMethodNerveTerminal = getInfo("threshold.method"); setOption("BlackBackground", false); run("Make Binary", "thresholded remaining black"); run("Despeckle"); saveAs("Tiff", axonFilepath); // Close template of nerve terminal after thresholding safeSelectWindow("C2-" + File.getName(templateFileName)); close(); safeSelectWindow("Threshold"); run("Close"); safeSelectWindow("C1-" + originalTitle); run("Threshold..."); // Adjust position of windows safeSelectWindow("C1-" + originalTitle); getLocationAndSize(x, y, width, height); setLocation(50, 50); safeSelectWindow("C1-" + File.getName(templateFileName)); getLocationAndSize(x2, y2, width2, height2); setLocation(100+width, 50); safeSelectWindow("C1-" + originalTitle); safeWaitForUser("3/7 Threshold Muscle Endplate. \nUse the reference image as a guide to selecting appropriate threshold. \nSelect a pre-set threshold from the drop-down menu or adjust manually (top scroll bar). \n-\nErase Background Noise. \nPaintbrush tool is already selected. \nBrush width can be adjusted by double clicking on brush icon. \n-\nPress OK. "); thresholdMethodEndplate = getInfo("threshold.method"); run("Make Binary", "thresholded remaining black"); run("Despeckle"); safeSelectWindow("Threshold"); run("Close"); safeSelectWindow("C1-" + originalTitle); saveAs("Tiff", endplateFilepath); // Close Template of Endplate after thresholding & delete temporary file safeSelectWindow("C1-" + File.getName(templateFileName)); close(); File.delete(templateFileName); if (!printIntermediateResults) { // The log was opened for the result of File.delete. Close it again safeSelectWindow('Log'); run('Close'); } setTool(4); safeSelectWindow(axonFilename); run("Set Measurements...", " redirect=None decimal=8"); safeWaitForUser("Screen 4/7 Measuring Axon Width. \nLine tool is already selected. \nMeasure by drawing line and pressing 'M' on keyboard. \nThree measurements are required: \n 1) Maximum axon width \n 2) Minimum axon width \n 3) Axon hillock width \n-\n\Hint: Axon hillock is where axon meets nerve terminals. \nHint: Zoom in for accurate measurement of thin axons. \nPress 'ctrl' and '+' or '-' to zoom in or out. \n-\nNotes: Axon width will be recorded automatically using the average of the three measurements. \nEnsure that extra measurements are deleted from 'results tab' before pressing OK. \nIf no axon is present, press OK. \n-\nPress OK."); resultsArray = newArray(); for (i=0; i