// Wrinkle Index ImageJ/FIJI Macro written by Brian Cosgrove, Mauck Lab // Last Revision: 8/2020 // Important Notes: This code was written to delineate contrast boundaries from z-stacks of Lamin-A/C staining featuring wrinkling features. // This code is not completely ideal for wide distribution, as it is not super flexible to new inputs. // If any questions arise as you use this macro, please feel free to contact me for help troubleshooting: bdcosgro (at) gmail (dot) com // Usage Notes: // 1) The *FeatureJ* plugin for ImageJ is required for this plugin to run // 2) Images to be analyzed need to have a set of increasing numbers at the end of the end of the filename (eg. "CTL_100x_2zoom_1.nd2" or "CTL_100x_2zoom-1.nd2" or "CTL_100x_2zoom1.nd2"). In the opening dialog box you will have to specify the image stem as the part of the image name before the number. // 3) Regularization Factor is meant to scale the thresholding algorithms for the brightness fluctuations between imaging days. This parameter **MUST** be kept the same between any images from the same imaging set and same exposure. // 4) Image stacks need to be sufficiently high resolution to generate good contrast boundaries // 5) Some inital tweaking might have to be done to set the regularization factor so that that the edge map properly matches the eye-test. Keep RF the same for imaging session after this point. // 6) ****Be careful of overwriting old output files if you re-run the code! Choose a unique name. // Input files: // a) An opened z-stack in ImageJ from a series of single channel z-stacks in a folder (used to pull details from) // b) All files to be analyzed must have increasing numbers at the end of the end of the filename (eg. "CTL_100x_2zoom_1.nd2", "CTL_100x_2zoom_2.nd2", etc.). Users will have to set the image name stem as all of the parts of the image name before the number. // Output files: // a) maximum projection image of the LMNA channel // b) binarized edge detection map // c) a data file containing all of the raw data for each image in the series //Pulling directory and filename from open file, then closing it inputDir1 = getDirectory("image"); filename = getTitle; close(); //Importing data for routine. Defaults can be changed below extension_default = ".nd2"; starting_image_default=1; end_image_default=20; offset_default=0; label_default="CTL"; regFactor_default=6; output_default="output"; Dialog.create("Wrinkle Index: Options"); Dialog.addNumber("Starting Image Number:", starting_image_default, 0, 3, ""); Dialog.addNumber("End Image Number (Must be >= starting number):", end_image_default, 0, 3, ""); Dialog.addNumber("Regularization Factor (higher RF is more sensitive for edge detection):", regFactor_default,0,4,""); Dialog.addString("Output Filename:", output_default, 25); Dialog.addString("Group Label:",label_default, 15) Dialog.addString("Filename Stem Before Numbering (eg. 'CTL_100x_2zoom_' for CTL_100x_2zoom_1.nd2 filename):",filename, 30) Dialog.addString("Image Extension:",extension_default, 8) Dialog.show(); starting_image = Dialog.getNumber(); end_image = Dialog.getNumber(); regFactor = Dialog.getNumber(); output= Dialog.getString(); label= Dialog.getString(); output=output+"_rF"+regFactor+"_"+label+".csv"; filename= Dialog.getString(); extension= Dialog.getString(); cell_images = 1+(end_image-starting_image); wrinkleIndex = newArray(cell_images); nuclearArea = newArray(cell_images); threshold= newArray(cell_images); minVal=newArray(cell_images); medianVal=newArray(cell_images); meanVal=newArray(cell_images); stdVal=newArray(cell_images); AR=newArray(cell_images); run("Set Measurements...", "area mean min median standard redirect=None decimal=3"); count=0; for (i=starting_image; i<=end_image; i++) { run("Bio-Formats Importer", "open=["+inputDir1+filename+i+extension+"] color_mode=Grayscale view=Hyperstack stack_order=XYCZT"); dir = getDirectory("image"); name=getTitle; // This z-projects all of the images in the stack and gets the title of each image. losing main stack window & naming sub windows run("Z Project...", "projection=[Max Intensity]"); title=getTitle; LaminAC=title; selectWindow(name); close(); /* title_split=getTitle; splitImage_name_root=substring(title_split,0,lengthOf(title_split)-1); DAPI="MAX_"+name; LaminAC=splitImage_name_root+"1"; */ // Thresholding the channels selectWindow(LaminAC); run("Enhance Contrast", "saturated=0.35"); // Create mask of total LaminAC signal selectWindow(LaminAC); run("Duplicate...", " "); run("Threshold..."); setAutoThreshold("Default dark"); setOption("BlackBackground", true); run("Convert to Mask"); selectWindow("Threshold"); run("Close"); /* run("Threshold..."); setAutoThreshold("Default dark"); setOption("BlackBackground", true); run("Convert to Mask"); selectWindow("Threshold"); run("Close"); */ // Change image to 8-bit and save a maximum projection image selectWindow(LaminAC); run("Duplicate...", " "); run("Enhance Contrast", "saturated=0.35"); run("8-bit"); saveAs("PNG",dir+label+"_Cell"+i+"_rF"+regFactor+"_MaxInt"); close(label+"_Cell"+i+"_rF"+regFactor+"_MaxInt.png"); // Selecting the center of the nuclei to generate the outer boundary of the nucles. run("Images to Stack"); Stack.setPosition(1,2,1) setTool("wand"); waitForUser("Click nuclei center, make sure nuclei is outlined or choose different point , then press ok"); run("Scale... ", "x=0.9 y=0.9 centered"); Stack.setPosition(1,1,1) // Get max/min intensity, mean and median pixel values, std dev of intensity, and the nuclear aspect ratio. run("Measure"); maxInt=getResult('Max',nResults-1); minInt=getResult('Min',nResults-1); mean=getResult('Mean',nResults-1); median=getResult('Median',nResults-1); stddev=getResult('StdDev',nResults-1); AR=getResult('Aspect_Ratio',nResults-1); IJ.deleteRows(0, nResults-1); getMinAndMax(min, max); run("Stack to Images"); // Scale input for Sobel edge detection based on the mean and std dev of pixel inputs scaled by the regularization factor selectWindow(LaminAC); maxInt=((mean)-(1.5*stddev))/regFactor; // Run sobdel-based edge detection run("FeatureJ Edges", "compute smoothing=0.95 suppress lower="+maxInt+" higher="+maxInt); saveAs("PNG",dir+label+"_Cell"+i+"_rF"+regFactor+"_Edges"); selectWindow(LaminAC); close(); run("Images to Stack"); // Select boundaries for binarized edge map and shrink slightly to remove the contrast outline of the nucleus. setTool("wand"); waitForUser("Click nuclei center, make sure nuclei is outlined or choose different point , then press ok"); run("Scale... ", "x=0.96 y=0.96 centered"); Stack.setPosition(1,2,1) // Calculate edge detection statistics run("Measure"); medianVal[count]=median; stdVal[count]=stddev; minVal[count]=minInt; threshold[count]=maxInt; meanVal[count]=mean; wrinkleIndex[count]=getResult('Mean',nResults-1)/255*100; // since binarized, divided by avg if every pixel was lit (/255) in percent (*100) nuclearArea[count]=getResult('Area',nResults-1); close("Stack"); count=count+1; } // Output and save data Array.show("Results",wrinkleIndex,nuclearArea,threshold,minVal,medianVal,meanVal,stdVal); saveAs("Results",dir+output);