├── .gitignore ├── README.md ├── img ├── screenshot01.png ├── screenshot02.png ├── screenshot03.png ├── screenshot04.png ├── screenshot05.png ├── screenshot06.png ├── screenshot07.png ├── screenshot08.png ├── screenshot09.png ├── screenshot10.png └── screenshot11.png ├── macros ├── AutoRun │ └── Load_qFunctions.ijm ├── quantixed │ └── qFunctions.txt └── toolsets │ └── Click Square ROI.txt └── scripts └── LabCode ├── About_LabCode.ijm ├── Blind_Analysis.ijm ├── ELN_Saver.ijm ├── Figure Maker ├── Change Montage Merge │ ├── RGB_to_MGB.ijm │ ├── RGB_to_MGW.ijm │ ├── RGB_to_OFP.ijm │ └── RGB_to_Whatever.ijm ├── Invert_Montage.ijm ├── Invert_Montage_Auto.ijm ├── Make_Montage.ijm ├── Make_Montage_Directory.ijm ├── Montage_Compiler.ijm ├── ROI_Zoom.ijm └── ROI_Zoom_External.ijm ├── Figure Utils └── Check_Colour_Blindness.ijm ├── Movie Annotation ├── Annotate_Movie.ijm ├── Irregular_Time_Stamps.ijm ├── Save_Time_Stamps_To_Text_File.ijm └── Save_Time_Stamps_To_Text_Files_Directory.ijm └── qUtils ├── Make_NIS_Pure_RGB.ijm ├── Print_Titles_Of_Nice_Images.ijm ├── mvd2_tools.ijm └── nd2_Save_As_Tiff.ijm /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | 4 | # Builds 5 | *.class 6 | /*.jar 7 | /build/ 8 | /target/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imagej-macros 2 | 3 | Some simple macros for Fiji/ImageJ that we are using in the lab. 4 | 5 | Most of these tools are available via the *quantixed* ImageJ [update site](http://sites.imagej.net/Quantixed/). Instructions for how to follow a 3rd party update site are [here](http://imagej.net/How_to_follow_a_3rd_party_update_site). This is the best way to install these macros and maintain the latest versions. 6 | 7 | If you want to install manually, add the contents of `macros` and `scripts` to the corresponding directories in your Fiji/ImageJ installation. 8 | 9 | After installation, all macros can be found under the menu item called **LabCode**. 10 | 11 | ## Quick links 12 | 13 | 1. [Figure Maker](#figure-maker) 14 | 1. Prepare your [images](#prepare-your-images) 15 | 2. Make a [montage](#montage) 16 | 3. Optional: add [ROI Zoom](#roi-zoom) 17 | 4. [Compile](#put-the-rows-together) your montages 18 | 5. [Figures the quantixed way](#figures-the-quantixed-way) 19 | 6. Typical figure [workflows](#workflows) - overview 20 | 2. [Blind Analysis](#blind-analysis) 21 | 3. [ELN Saver](#eln-saver) 22 | 4. [Other Utilities](#other-utilities) 23 | 5. [I'm getting errors](#troubleshooting) 24 | 25 | 26 | ### Figure Maker 27 | 28 | There are macros to help you to make figures with montages - made the way we like them! There are other plugins for making figures in ImageJ but none did what we [wanted](#figures-the-quantixed-way), so we made our own. 29 | 30 | There is a video explaining how to use the Figure Maker [here](https://www.youtube.com/watch?v=6qORVjkCm90). 31 | 32 | 33 | #### Prepare your images 34 | 35 | Load in your multichannel image, adjust how you want and then crop to a square. 36 | To do this select *Click Square ROI* from the >> on the toolbar. 37 | 38 | ![fm001](img/screenshot01.png) 39 | 40 | This tool gives 400 x 400 pixel square ROI, right click to get a different size. 41 | Select the area you want. 42 | 43 | ![fm002](img/screenshot02.png) 44 | 45 | Crop your images and save them as TIFFs. 46 | 47 | ![fm003](img/screenshot03.png) 48 | 49 | 50 | #### Make a montage 51 | 52 | To make a nicely spaced montage (row of images). Select *LabCode > Figure Maker > Make Montage* 53 | 54 | You are asked to pick your cropped TIFF. 55 | 56 | You can specify the number of grayscale (channel) panels and choose the number of merges. 57 | 58 | ![fm004](img/screenshot04.png) 59 | 60 | In the next dialog you can pick which panels go where in your montage and how to merge channels using which colours. 61 | 62 | You can also specify a vertical (column) montage rather than horizontal (row). 63 | 64 | Grout and scale bars can be added flexibly, no border is added. This is different to ImageJ's Make Montage and makes figure rows the [way we like them](figures-the-quantixed-way)! Note that if you are going to compile montages, it's best to add a scale bar at this stage to just one of the images. 65 | 66 | ![fm005](img/screenshot05.png) 67 | 68 | Your montage is saved in the same directory as the original image. The macro leaves it displayed, so that you can admire your awesome data! 69 | 70 | ![fm006](img/screenshot06.png) 71 | 72 | #### Multiple montages 73 | 74 | If you want to make many montages, then select *LabCode > Figure Maker > Make Montage Directory*. This macro allows the user to make montages from all TIFFs in a directory. A dialog is shown at the start that allows the user to select which channels go where and then the montages are made in batch mode. 75 | 76 | #### Movie/Stack montages 77 | 78 | It is now possible to make a montage of movies or stacks. Select *LabCode > Figure Maker > Make Montage* and point the dialog at the file you'd like to montage. 79 | 80 | If the image is a movie, or a z-stack (but not both) comprising more than one channel, then you will be able to generate a movie/stack montage. 81 | 82 | #### Optional: add ROIs and zooms 83 | 84 | Sometimes, we like to add a ROI and a zoomed version of this ROI to various panels in the montage. To do this open your montage and select *LabCode > Figure Maker > ROI Zoom* 85 | 86 | ![fm007](img/screenshot07.png) 87 | 88 | You can pick which corner you want the zoom and which panels you'd like to add an ROI and zoom. 89 | 90 | After clicking OK, you are asked to select the *centre* of the ROI. 91 | 92 | ![fm008](img/screenshot08.png) 93 | 94 | The resulting image is saved in the same directory as the montage. 95 | 96 | ![fm009](img/screenshot09.png) 97 | 98 | Note that this code will run on stacks as well so that you can add ROIs and zooms to movies! 99 | 100 | It only works on square images and montages made from squares. If your zooms are in the wrong place, check the size(s) of your input images. 101 | 102 | When the ROI-Zoom version is made, a text file is saved in the same directory as the resulting image. It contains useful information that you can use to recreate the image if you need to. 103 | 104 | Alternatively, you might prefer to highlight the ROI but not insert the zoomed ROI. 105 | In this case, **Make ROI Zoom External** will allow you to save the zoomed ROI as a separate panel. 106 | If you'd like to place the zoomed ROI as a further panel, select 0 to zoom the ROI so that it is the same size as a regular panel; otherwise, pick the magnification you'd prefer. 107 | 108 | #### Optional: invert panels or use non-standard LUTs 109 | 110 | The grayscale panels might look better inverted so that they are black-on-white, rather than white-on black. 111 | This can be helpful for dim or small features. 112 | To do this, open the montage that you would like to invert and select ***Invert Montage***. 113 | This will launch a dialog to help you to choose which panels you'd like to invert and whether you would like to outline the panels with a black border. 114 | It's best to do this after you have made the ROI zooms if you want to. 115 | There's also an "auto" mode, that will invert all panels except the right/bottom panel (contains the merge) to speed things up. 116 | 117 | If you'd like to switch an RGB merge panel for the a different colour scheme, open the montages you'd like to change and select **Change Montage Merge**. 118 | There are some options to switch RGB to OFP (Orange-Fresh-Purple) or to MGB (Magenta-Green-Blue). 119 | It is possible to specify any combination of RGB, CMY, W(hite) or OFP. 120 | Just select the "Whatever" option and type in your desired combination to map RGB to. 121 | This will work on all open images so it is very simple to transform a whole bunch of montages in the same way. 122 | It does not work on compiled montages. 123 | Note: to run this, you must have _NeuroCytoLUTs_ Update Site installed. 124 | 125 | #### Now put the rows together 126 | 127 | Finally, if we have more than one montage, we need to compile them together. Load in all the montages you'd like to compile. Now select *LabCode > Figure Maker > Montage Compiler* 128 | 129 | The dialog asks you to select which montage you'd like where. 130 | 131 | The routine is intelligent enough to recognise vertical and horizontal montages and to compile them left-to-right or top-to-bottom as appropriate. 132 | 133 | Again, a text file is saved with the resulting compilation image so that you know which images went where. 134 | 135 | ![fm0010](img/screenshot10.png) 136 | 137 | Your compilation will save back in the same directory as the montages. Note that you can use this macro to make one compilation and then use it again to add more montages (extra rows) or other compilations. Very large compilation can be built this way. 138 | 139 | ![fm0011](img/screenshot11.png) 140 | 141 | Note that **results from Figure Maker are saved as a flat 300 ppi RGB TIFF** ready for use in Illustrator or equivalent software. 142 | 143 | Important note about scale bars. The code suggests a pixel size for use in the scale bar. If the scaling of your images is incorrect, then the scale bar will be the wrong size. **If the dialog box says 0.069, double-check your original image for scaling information**, 0.069 is the "pixel size" for a 300 ppi image, i.e. the scaling no longer relates to the original image. Bottom line is: **make sure you know the pixel size of your images before adding any scale bars.** 144 | 145 | #### Figures the quantixed way 146 | 147 | For multichannel microscopy images, e.g. from an immunofluorescence experiment, *quantixed* follows these rules for best practice. 148 | 149 | 1. Individual channels as grayscale - reason: the eye does not detect black-to-red in the same way as black-to-green or black-to-blue 150 | 2. In a row montage the merge is on the right. In a column montage it is at the bottom 151 | 3. Square images, square ROIs and square zooms 152 | 4. No border 153 | 5. Scale bar in the bottom right corner (added to the compiled montage) 154 | 4. Fixed grout of 8 pixels (suggested) 155 | 6. Scale bar of 10 µm, height of 2 x grout (suggested) 156 | 7. Grouting between conditions is 2 x grout between channels (suggested) 157 | 8. Labelling is done in Illustrator or some other software to assemble the final figure, *not* in ImageJ 158 | 159 | #### Workflows 160 | 161 | The basic workflow is to go: 162 | 163 | ``` 164 | IMAGE(S) 165 | | 166 | | (Montage Maker or Montage Maker Directory) 167 | v 168 | MONTAGE(S) 169 | | 170 | | (Montage Compiler) 171 | v 172 | COMPILED MONTAGE 173 | ``` 174 | Images can be XYC, XYCT or XYCZ. The Montage is save as RGB and will be XY, XYT or XYZ, respectively. 175 | 176 | Montages can be modified in the following ways: 177 | 178 | - ROI zoom or ROI zoom external - can be 1 or more times 179 | - Inverted 180 | - Change colors 181 | 182 | These steps can be run iteratively, e.g. ROI zooms twice, then invert, then change colors. 183 | Montages and modified montages can be compiled together as long as the dimensions are compatible. 184 | 185 | ROI Zoom macros and Compile Montage require images open to work with, the other macros typically do not allow open images, and you need to find the source image upon running. 186 | 187 | 188 | ### Figure Utilities 189 | 190 | Check whether your figure or image is colour blind safe using this simple utility `Check_Colour_blindess.ijm`. 191 | 192 | ### Blind Analysis 193 | 194 | `BlindAnalysis.ijm` Takes a directory of TIFFs, strips the label from them and saves them with a blinded name. A tsv called `log.txt` is created to log the association between the original file and the blinded copy. Works on TIFF only. 195 | 196 | 197 | ### ELN Saver 198 | 199 | This is a simple utility to save a version of the file you are viewing in Fiji/ImageJ to put in your electronic lab notebook. A png version or low-res movie of the file you are looking at is saved to the Desktop with a unique name. A text file is saved to explain the filenames. 200 | 201 | ### Movie Annotation 202 | 203 | Two macros that will: 204 | 205 | 1. Add arrows to movies to highlight and track an object of interest. Unlike other scripts out there, this one places an arrowhead in a fixed orientation/distance from the object and allows annotations in any frame (not _all_ frames). 206 | 2. Irregular Time Stamps - adds timestamps to movies, formatting them in hh:mm or mm:ss. Unlike other versions, negative times are allowed and the time intervals do not have to be regular. Requires a new line separated text file of times. 207 | 208 | -- 209 | 210 | ### Other utilities 211 | 212 | In `qUtils` there are some other utility macros that we use in the lab. 213 | 214 | We've had problems with nd2 files being saved with a LUT that is not pure red, pure green or pure blue. This is because it uses a LUT that is based on absolute wavelengths of the fluorophore. You can use `Make_NIS_Pure_RGB.ijm` to sort out the colours. 215 | 216 | Open all the nd2 files in a directory and save them as TIFF to another directory with `nd2SaveAsTiff.ijm`. 217 | 218 | Maybe you like to open a whole directory of images, look through them, closing the bad ones and leaving the good ones open. Perhaps you want to grab the list of good images so that you can come back to it later? Well, `PrintTitlesOfNiceImages.ijm` does this for you. 219 | 220 | -- 221 | 222 | ### Troubleshooting 223 | 224 | Most problems are solved by allowing Fiji's updater to install the latest code from the *quantixed* ImageJ Update Site. 225 | 226 | **Still not working?** All routines use a number of custom functions which get loaded when Fiji starts up. Errors in running these routines usually traceback to third party code that modifies `StartupMacros.fiji.ijm`. Which can be found in the `macros` directory. Specifically, if the third party has deleted this code block, then none of the autorun functions happen at startup. 227 | 228 | ```// The macro named "AutoRun" runs when ImageJ starts. 229 | 230 | macro "AutoRun" { 231 | // run all the .ijm scripts provided in macros/AutoRun/ 232 | autoRunDirectory = getDirectory("imagej") + "/macros/AutoRun/"; 233 | if (File.isDirectory(autoRunDirectory)) { 234 | list = getFileList(autoRunDirectory); 235 | // make sure startup order is consistent 236 | Array.sort(list); 237 | for (i = 0; i < list.length; i++) { 238 | if (endsWith(list[i], ".ijm")) { 239 | runMacro(autoRunDirectory + list[i]); 240 | } 241 | } 242 | } 243 | } 244 | ``` 245 | 246 | If you can't bear to uninstall the third-party code (or don't know which update site causes the problem), just paste that code block into `StatupMacros.fiji.ijm` and things will start to work again. 247 | 248 | **Other notes** Note that usage of RGB images has a limitation of a filename length of 59 characters. 249 | 250 | **The grout looks really tiny and the scale bar is very thin** The suggested grout sizes work well for 400x400 images. 251 | If you have 1000x1000 images the suggested grouts will look thin when imported into a figure, adjust accordingly. 252 | As a guide, a 600x600 image that has two panels and one merge with a grout of 8 pixels will be 1816 pixels wide. 253 | At 300 dpi this is a final size of 155 mm. 254 | A full size figure is usually 170 mm, so the figure will be ~91% of the full width of the figure. 255 | If you shrink it to half size, now the grout will be 4 pixels or 0.34 mm which will look quite thin. 256 | A rule of thumb is grout should be 1/50th of the image width, but ultimately, test it out! 257 | 258 | -------------------------------------------------------------------------------- /img/screenshot01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot01.png -------------------------------------------------------------------------------- /img/screenshot02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot02.png -------------------------------------------------------------------------------- /img/screenshot03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot03.png -------------------------------------------------------------------------------- /img/screenshot04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot04.png -------------------------------------------------------------------------------- /img/screenshot05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot05.png -------------------------------------------------------------------------------- /img/screenshot06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot06.png -------------------------------------------------------------------------------- /img/screenshot07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot07.png -------------------------------------------------------------------------------- /img/screenshot08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot08.png -------------------------------------------------------------------------------- /img/screenshot09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot09.png -------------------------------------------------------------------------------- /img/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot10.png -------------------------------------------------------------------------------- /img/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantixed/imagej-macros/94a28bd13ea6614fec554a4979158e112d5240f5/img/screenshot11.png -------------------------------------------------------------------------------- /macros/AutoRun/Load_qFunctions.ijm: -------------------------------------------------------------------------------- 1 | /***** Installs custom functions from folder ***** 2 | * To load at startup, this macro file should reside inside /macros/AutoRun/ 3 | * 4 | * Nicolas De Francesco (image.sc @NicoDF), July 2019 5 | * Scanning folder trick taken from Fiji's AutorRun_Scripts.ijm 6 | */ 7 | 8 | custom_folder="/quantixed/"; // modify to suit your needs. The folder should be inside the macros folder. 9 | 10 | functionDirectory = getDirectory("macros") + custom_folder; 11 | if (File.isDirectory(functionDirectory)) { 12 | list = getFileList(functionDirectory); 13 | for (i=0; i theWidth) vChoice = true; 568 | len = imgArray.length; 569 | 570 | // Standard sizes 571 | grout = 16; 572 | res = 300; 573 | sblen = 10; 574 | mag = 0.069; 575 | // Make dialog box 576 | Dialog.create("Compile the montages"); 577 | // variations based on number of files 578 | if (vChoice == true) { 579 | Dialog.addMessage("Detected columns.\rSelect order for your compilation, left to right"); 580 | } 581 | else { 582 | Dialog.addMessage("Detected rows.\rSelect order for your compilation, top to bottom"); 583 | } 584 | for (i = 0; i < len; i ++) { 585 | labStr = d2s(i+1,0); 586 | Dialog.addChoice(labStr, rowArray); 587 | } 588 | Dialog.addNumber("Row gap (px, default = 2 x grout):", 16); 589 | Dialog.addNumber("dpi", 300); 590 | Dialog.addCheckbox("Include scale bar?", false); 591 | Dialog.addNumber("Scale bar size (µm):", 10); 592 | Dialog.addNumber("Scaling, 1 px is how many µm?", 0.069); 593 | Dialog.addCheckbox("Force other orientation:", false); 594 | Dialog.show(); 595 | // variations based on number of files 596 | for (i = 0; i < len; i ++) { 597 | nameArray[i] = Dialog.getChoice(); 598 | } 599 | grout = Dialog.getNumber(); 600 | res = Dialog.getNumber(); 601 | sbchoice = Dialog.getCheckbox(); 602 | sblen = Dialog.getNumber(); 603 | mag = Dialog.getNumber(); 604 | forceChoice = Dialog.getCheckbox(); 605 | // forceChoice reverses the logic of vChoice 606 | if(vChoice == true && forceChoice == true) vChoice = false; 607 | else if(vChoice == false && forceChoice == true) vChoice = true; 608 | // decisions collected 609 | setBatchMode(true); 610 | 611 | // setup for save 612 | win = getTitle(); 613 | dir1 = getDirectory("image"); 614 | newName = "cmp" + len + win; 615 | 616 | // get dimensions 617 | wArray = newArray(len); 618 | hArray = newArray(len); 619 | hPosArray = newArray(len+1); 620 | hPosArray[0] = 0; 621 | width = 0; 622 | height = 0; 623 | for (i=0; i 1 && ff > 1) exit ("Reduce dimensions before making montage"); 711 | if (cc > 1 && ss * ff == 1) checker = true; 712 | // if cc is 1 or more AND either ss or ff is > 1; checker stays false 713 | } else if (bitDepth() == 24 && ss * ff == 1) { 714 | run("Split Channels"); 715 | run("Images to Stack"); 716 | checker = true; 717 | } else exit ("Input image does not meet requirements for montage"); 718 | return checker; 719 | } 720 | 721 | function getListOfImages() { 722 | numImages = nImages; 723 | winArray = newArray(numImages); 724 | for (i = 0; i < numImages; i ++) { 725 | selectImage(i + 1); 726 | winArray[i] = getTitle(); 727 | // ensure grayscale while we are here 728 | run("Grays"); 729 | } 730 | return winArray; 731 | } 732 | 733 | function convertChoicesToWindows(choiceArray,winArray) { 734 | convertedArray = newArray(choiceArray.length); 735 | for (i = 0; i < choiceArray.length; i ++) { 736 | arrayItem = choiceArray[i]; 737 | if (choiceArray[i] == "*None*") { 738 | convertedArray[i] = "*None*"; 739 | } else { 740 | lookupInteger = parseInt(replace(arrayItem,"C","")); 741 | convertedArray[i] = winArray[lookupInteger - 1]; 742 | } 743 | } 744 | return convertedArray; 745 | } 746 | 747 | function generateMontage(newName, vChoice, gVar, mVar, width, height, grout, res, sbchoice, sblen, mag, gNameArray, m1NameArray, m2NameArray) { 748 | len = gVar + mVar; 749 | newImage(newName, "RGB", ((width * len) + (grout * (len - 1))), height, 1); 750 | // paste in grayscales 751 | for (j = 0; j < gVar; j++) { 752 | wName = gNameArray[j]; 753 | selectImage(wName); 754 | run("Copy"); 755 | selectImage(newName); 756 | makeRectangle((width * j) + (grout * j), 0, width, height); 757 | run("Paste"); 758 | } 759 | 760 | // build mergeString(s) 761 | merge1String = ""; 762 | merge2String = ""; 763 | 764 | for (i = 0; i < 7; i++) { 765 | if (mVar > 0) merge1String += "c" + d2s(i+1,0) + "=[" + m1NameArray[i] + "] "; 766 | if (mVar == 2) merge2String += "c" + d2s(i+1,0) + "=[" + m2NameArray[i] + "] "; 767 | } 768 | merge1String += "keep"; 769 | merge2String += "keep"; 770 | // make array to hold merge names 771 | mImgArray = newArray("merge1","merge2"); 772 | 773 | // paste in the merge(s) 774 | for (i = 0; i < mVar; i++) { 775 | if (i == 0) { 776 | run("Merge Channels...", merge1String); 777 | rename("merge1"); 778 | } else { 779 | run("Merge Channels...", merge2String); 780 | rename("merge2"); 781 | } 782 | selectImage(mImgArray[i]); 783 | run("Copy"); 784 | selectImage(newName); 785 | makeRectangle(((width * gVar) + (grout * gVar)) + ((width * i) + (grout * i)), 0, width, height); 786 | run("Paste"); 787 | } 788 | selectImage(newName); 789 | // rotate right? 790 | if (vChoice == true) 791 | run("Rotate 90 Degrees Right"); 792 | 793 | // add scale bar (height of bar is same as grout)? 794 | if (sbchoice==true) { 795 | getDimensions(w, h, c, slices, frames); 796 | setColor(255,255,255); 797 | fillRect(w - (grout + (sblen / mag)), h - (2 * grout), sblen / mag, grout); 798 | } 799 | // specify dpi default is 300 dpi 800 | run("Set Scale...", "distance=" + res + " known=1 unit=inch"); 801 | run("Select None"); 802 | } 803 | 804 | // -------------------------------------------------------------------------------- 805 | // Change Montage Merge Functions 806 | // -------------------------------------------------------------------------------- 807 | 808 | function autoChangeMontage(lutstring) { 809 | // assumes merge is on the right/bottom and therefore not inverted 810 | lutarray = makeLutArray(lutstring); 811 | custarray = makeCustArray(lutstring); 812 | lutdir = getDirectory("luts"); 813 | title = getTitle(); 814 | dir1 = getDirectory("image"); 815 | newName = "RGB2" + lutstring + "_" + title; 816 | run("Select None"); 817 | getDimensions(w, h, c, numSlices, nFrames); 818 | // deal with multiple frames or slices 819 | nShots = maxOf(numSlices,nFrames); 820 | if (numSlices > 1 && nFrames > 1) exit("Number of slices and frames is greater than one"); 821 | if (numSlices == 1) sliceOrFrame = 0; 822 | if (numSlices > 1) sliceOrFrame = 1; 823 | if (nFrames > 1) sliceOrFrame = 2; 824 | 825 | // work out grout size 826 | if (w == h) { 827 | nCol = 1; 828 | nRow = 1; 829 | grout = 0; 830 | nPanel = 1; 831 | vChoice = ""; 832 | ww = w; 833 | hh = h; 834 | } 835 | else if (w > h) { 836 | nCol = floor(w/h); 837 | nRow = 1; 838 | grout = (w - (nCol * h)) / (nCol - 1); 839 | nPanel = nCol; 840 | vChoice = ""; 841 | ww = h; 842 | hh = h; 843 | } 844 | else { 845 | nCol = 1; 846 | nRow = floor(h/w); 847 | grout = (h - (nRow * w)) / (nRow - 1); 848 | nPanel = nRow; 849 | vChoice = "vert"; 850 | ww = w; 851 | hh = w; 852 | } 853 | 854 | // collect decisions 855 | panelDecisions = newArray(nPanel); 856 | for (i=0; i 1) { 50 | stripFrameByFrame(totalSlices); 51 | } else { 52 | setMetadata("Label", ""); // strips the label data from the image for blinding purposes 53 | } 54 | if(!endsWith(outputPathPerm, ".tif")) { 55 | outputPathPerm = outputPathPerm + ".tif"; 56 | } 57 | saveAs("TIFF",outputPathPerm); 58 | print(f,imNames[i]+"\t"+imPermNames[i]); 59 | close(); 60 | } 61 | setBatchMode("exit and display"); 62 | showStatus("finished"); 63 | } 64 | 65 | // strips the label data from each slice of an image 66 | function stripFrameByFrame(totalSlices) { 67 | for(i = 0; i < totalSlices; i ++){ 68 | setSlice(i+1); 69 | setMetadata("Label", ""); 70 | } 71 | } 72 | 73 | // function that adds a variable to an array 74 | function append(arr, value) { 75 | arr2 = newArray(arr.length + 1); 76 | for (i = 0; i < arr.length; i ++) 77 | arr2[i] = arr[i]; 78 | arr2[arr.length] = value; 79 | return arr2; 80 | } 81 | -------------------------------------------------------------------------------- /scripts/LabCode/ELN_Saver.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * Macro to save a "small" version of the top window to 3 | * the desktop for use in an electronic lab notebook. 4 | * This is the first version - expect bugs! 5 | */ 6 | 7 | macro "Make Version For ELN" { 8 | if (nImages < 1) exit ("I need an image to do this."); 9 | setBatchMode(true); 10 | title1 = getTitle(); 11 | dir1 = getDirectory("image"); 12 | id1 = getImageID(); 13 | dir2 = getDirectory("home")+"Desktop"+File.separator+"ELNoutput"+File.separator; 14 | // make this directory on the desktop if it doesn't already exist 15 | File.makeDirectory(dir2); 16 | // width, height, channels, slices, frames 17 | getDimensions(ww, hh, cc, ss, ff); 18 | slice1 = getSliceNumber(); 19 | TimeString = getTimeDate(); 20 | // duplicate window 21 | if(ss > 1 || ff > 1) { 22 | //run("Duplicate...", "title=elnout duplicate range=1-" + ff); // possibility to add movie option here 23 | run("Duplicate...", "title=elnout"); 24 | } else { 25 | run("Duplicate...", "title=elnout"); 26 | } 27 | id2 = getImageID(); 28 | // do something about size 29 | if(ww > 2000 || hh > 2000) { 30 | wFactor = -floor(-ww / 2000); 31 | hFactor = -floor(-hh / 2000); 32 | allFactor = maxOf(2,maxOf(wFactor,hFactor)); // at least do 50% 33 | run("Size...", "width=" + round(ww / allFactor) + " height=" + round(hh / allFactor) + " depth=1 constrain average interpolation=Bilinear"); 34 | } 35 | // downsample if necessary 36 | if (bitDepth==24) 37 | run("8-bit Color", "number=256"); 38 | else 39 | run("8-bit"); 40 | newName = TimeString + ".png"; 41 | saveAs("png",dir2+newName); 42 | close(); 43 | // record what happened 44 | print("\\Clear"); 45 | print(title1 + " from " + dir1 + "\rexported as " + TimeString); 46 | selectWindow("Log"); 47 | saveAs("text", dir2+newName+"_note.txt"); 48 | setBatchMode(false); 49 | } 50 | function getTimeDate() { 51 | // Make unique filename. 52 | // ISO 8601 with no special characters is 53 | // YYYYMMDDTHHMMSSZ e.g. 20170630T193338Z 54 | getDateAndTime(year, month, dayOfWeek, dayOfMonth, hour, minute, second, msec); 55 | TimeString = "UTC"+year; 56 | if (month<10) {TimeString = TimeString+"0";} 57 | TimeString = TimeString+month; 58 | if (dayOfMonth<10) {TimeString = TimeString+"0";} 59 | TimeString = TimeString+dayOfMonth; 60 | if (hour<10) {TimeString = TimeString+"0";} 61 | TimeString = TimeString+"T"+hour; 62 | if (minute<10) {TimeString = TimeString+"0";} 63 | TimeString = TimeString+minute; 64 | if (second<10) {TimeString = TimeString+"0";} 65 | TimeString = TimeString+second+"Z"; 66 | TimeString = replace(TimeString,"UTC",""); 67 | //showMessage(TimeString); 68 | return TimeString; 69 | } 70 | -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Change Montage Merge/RGB_to_MGB.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * The montage macros make a merge from the standard colours. 3 | * If a nonstandard colourset is required, this macro will change the merge panel. 4 | * It takes the RGB channels and changes them to e.g. Magenta, Green, Blue 5 | * Will work on all open images. 6 | * Limitation: will only change the right (or bottom) panel, i.e. if two merges are present, only far one is changed. 7 | * Limitation: assumes that the merge is not inverted. 8 | * Limitation: only works on montage and not compiled montages. 9 | * Idea is to 1) make a montage, 2) add ROI zooms, 3) change the merge panel, 4) compile montages 10 | * The other use is to switch colours when 1 and 2 have been done on many images. 11 | * http://github.com/quantixed/imagej-macros/ 12 | */ 13 | 14 | macro "RGB to MGB" { 15 | if (nImages == 0) exit("No image open"); 16 | setBatchMode(true); 17 | do{ 18 | autoChangeMontage("MGB"); 19 | } while (nImages > 0); 20 | setBatchMode(false); 21 | } -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Change Montage Merge/RGB_to_MGW.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * The montage macros make a merge from the standard colours. 3 | * If a nonstandard colourset is required, this macro will change the merge panel. 4 | * It takes the RGB channels and changes them to e.g. Magenta, Green, Blue 5 | * Will work on all open images. 6 | * Limitation: will only change the right (or bottom) panel, i.e. if two merges are present, only far one is changed. 7 | * Limitation: assumes that the merge is not inverted. 8 | * Limitation: only works on montage and not compiled montages. 9 | * Idea is to 1) make a montage, 2) add ROI zooms, 3) change the merge panel, 4) compile montages 10 | * The other use is to switch colours when 1 and 2 have been done on many images. 11 | * http://github.com/quantixed/imagej-macros/ 12 | */ 13 | 14 | macro "RGB to MGW" { 15 | if (nImages == 0) exit("No image open"); 16 | setBatchMode(true); 17 | do{ 18 | autoChangeMontage("MGW"); 19 | } while (nImages > 0); 20 | setBatchMode(false); 21 | } 22 | -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Change Montage Merge/RGB_to_OFP.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * The montage macros make a merge from the standard colours. 3 | * If a nonstandard colourset is required, this macro will change the merge panel. 4 | * It takes the RGB channels and changes them to e.g. Magenta, Green, Blue 5 | * Can also be used with non-standard LUTs e.g. Orange, Fresh, Purple (OFP). 6 | * Must have the NeuroCytoLUTs installed - macro checks for this. 7 | * Will work on all open images. 8 | * Limitation: will only change the right (or bottom) panel, i.e. if two merges are present, only far one is changed. 9 | * Limitation: assumes that the merge is not inverted. 10 | * Limitation: only works on montage and not compiled montages. 11 | * Idea is to 1) make a montage, 2) add ROI zooms, 3) change the merge panel, 4) compile montages 12 | * The other use is to switch colours when 1 and 2 have been done on many images. 13 | * http://github.com/quantixed/imagej-macros/ 14 | */ 15 | 16 | macro "RGB to OFP" { 17 | if (nImages == 0) exit("No image open"); 18 | if (lutCheck() == 0) exit("NeuroCytoLUTs are missing"); 19 | setBatchMode(true); 20 | do{ 21 | autoChangeMontage("OFP"); 22 | } while (nImages > 0); 23 | setBatchMode(false); 24 | } -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Change Montage Merge/RGB_to_Whatever.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * The montage macros make a merge from the standard colours. 3 | * If a nonstandard colourset is required, this macro will change the merge panel. 4 | * It takes the RGB channels and changes them to e.g. Magenta, Green, Blue 5 | * Can also be used with non-standard LUTs e.g. Orange, Fresh, Purple (OFP). 6 | * Must have the NeuroCytoLUTs installed - macro checks for this. 7 | * Will work on all open images. 8 | * Limitation: will only change the right (or bottom) panel, i.e. if two merges are present, only far one is changed. 9 | * Limitation: assumes that the merge is not inverted. 10 | * Limitation: only works on montage and not compiled montages. 11 | * Idea is to 1) make a montage, 2) add ROI zooms, 3) change the merge panel, 4) compile montages 12 | * The other use is to switch colours when 1 and 2 have been done on many images. 13 | * http://github.com/quantixed/imagej-macros/ 14 | */ 15 | 16 | macro "RGB to Whatever" { 17 | if (nImages == 0) exit("No image open"); 18 | if (lutCheck() == 0) exit("NeuroCytoLUTs are missing"); 19 | // make dialog for text entry 20 | colourString = getString("Colour string e.g. MGY", "RGB"); 21 | 22 | setBatchMode(true); 23 | do{ 24 | autoChangeMontage(colourString); 25 | } while (nImages > 0); 26 | setBatchMode(false); 27 | } -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Invert_Montage.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * Inverted grayscale panels can enhance dim features. 3 | * This macro enables the user to invert the grayscale channels of a montage 4 | * Idea is to 1) make a montage, 2) add ROI zooms, 3) do inversion, 4) compile montages 5 | * http://github.com/quantixed/imagej-macros/ 6 | */ 7 | 8 | macro "Invert Montage Panels" { 9 | if (nImages > 1) exit ("Use a single image or single montage"); 10 | if (nImages == 0) exit("No image open"); 11 | 12 | imageID = getImageID(); 13 | title = getTitle(); 14 | dir1 = getDirectory("image"); 15 | newName = "invert_" + title; 16 | run("Select None"); 17 | getDimensions(w, h, c, numSlices, nFrames); 18 | // deal with multiple frames or slices 19 | nShots = maxOf(numSlices,nFrames); 20 | if (numSlices > 1 && nFrames > 1) exit("Number of slices and frames is greater than one"); 21 | if (numSlices == 1) sliceOrFrame = 0; 22 | if (numSlices > 1) sliceOrFrame = 1; 23 | if (nFrames > 1) sliceOrFrame = 2; 24 | 25 | // work out grout size 26 | if (w == h) { 27 | nCol = 1; 28 | nRow = 1; 29 | grout = 0; 30 | nPanel = 1; 31 | vChoice = ""; 32 | ww = w; 33 | hh = h; 34 | } 35 | else if (w > h) { 36 | nCol = floor(w/h); 37 | nRow = 1; 38 | grout = (w - (nCol * h)) / (nCol - 1); 39 | nPanel = nCol; 40 | vChoice = ""; 41 | ww = h; 42 | hh = h; 43 | } 44 | else { 45 | nCol = 1; 46 | nRow = floor(h/w); 47 | grout = (h - (nRow * w)) / (nRow - 1); 48 | nPanel = nRow; 49 | vChoice = "vert"; 50 | ww = w; 51 | hh = w; 52 | } 53 | // dialog for choices 54 | labels = newArray(nPanel); 55 | defaults = newArray(nPanel); 56 | panelDecisions = newArray(nPanel); 57 | for (i=0; i 20 * grout && grout > 0) exit("Use a smaller stroke size"); 81 | 82 | setBatchMode(true); 83 | // border will be black if colorChoice is True 84 | if (colorChoice) { 85 | if (bitDepth() == 8) setColor(0); 86 | if (bitDepth() == 24) setColor(0,0,0); 87 | if (bitDepth() == 16) setColor(0); 88 | } 89 | 90 | for (i=0; i 0); 14 | setBatchMode(false); 15 | } 16 | 17 | function autoInvertMontage() { 18 | // use default options to invert panels in a montage 19 | // inverts all grayscale panels, assumes merge is on the right/bottom and therefore not inverted 20 | // black border of 4 pixels around inverted panels 21 | 22 | imageID = getImageID(); 23 | title = getTitle(); 24 | dir1 = getDirectory("image"); 25 | newName = "invert_" + title; 26 | run("Select None"); 27 | getDimensions(w, h, c, numSlices, nFrames); 28 | // deal with multiple frames or slices 29 | nShots = maxOf(numSlices,nFrames); 30 | if (numSlices > 1 && nFrames > 1) exit("Number of slices and frames is greater than one"); 31 | if (numSlices == 1) sliceOrFrame = 0; 32 | if (numSlices > 1) sliceOrFrame = 1; 33 | if (nFrames > 1) sliceOrFrame = 2; 34 | 35 | // work out grout size 36 | if (w == h) { 37 | nCol = 1; 38 | nRow = 1; 39 | grout = 0; 40 | nPanel = 1; 41 | vChoice = ""; 42 | ww = w; 43 | hh = h; 44 | } 45 | else if (w > h) { 46 | nCol = floor(w/h); 47 | nRow = 1; 48 | grout = (w - (nCol * h)) / (nCol - 1); 49 | nPanel = nCol; 50 | vChoice = ""; 51 | ww = h; 52 | hh = h; 53 | } 54 | else { 55 | nCol = 1; 56 | nRow = floor(h/w); 57 | grout = (h - (nRow * w)) / (nRow - 1); 58 | nPanel = nRow; 59 | vChoice = "vert"; 60 | ww = w; 61 | hh = w; 62 | } 63 | 64 | // collect decisions 65 | panelDecisions = newArray(nPanel); 66 | for (i=0; i 0) exit ("Please close all open images"); 20 | filepath = File.openDialog("Select a File"); 21 | open(filepath); 22 | dir1 = getDirectory("image"); 23 | // determine what we are dealing width 24 | getDimensions(ww, hh, cc, ss, ff); 25 | win = getTitle(); 26 | okVar = checkImageForMontage(win); 27 | if (okVar == true) { 28 | montageMaker(dir1); 29 | } else if (okVar == false) { 30 | rename("mmTemp"); 31 | dir = getDir("temp"); 32 | if (dir == "") 33 | exit("No temp directory available"); 34 | // check temporary directory for temporary montage files, delete if found 35 | qCheckForTempFiles(dir); 36 | // split out stack into separate files in temporary directory 37 | qSaveImageSequence(dir); 38 | montageMakerMulti(dir, dir1, false, win); 39 | } 40 | } 41 | 42 | function qCheckForTempFiles(dir) { 43 | list = getFileList(dir); 44 | tiffnum = 0; 45 | for (i = 0; i < list.length; i ++) { 46 | if (startsWith(list[i], "mmTemp") && endsWith(toLowerCase(list[i]), ".tif")) { 47 | tiffnum = tiffnum + 1; 48 | ok = File.delete(dir + list[i]); 49 | } 50 | } 51 | } 52 | 53 | function qSaveImageSequence(dir) { 54 | win = getTitle(); 55 | setBatchMode(true); 56 | 57 | getDimensions(ww, hh, cc, ss, ff); 58 | if(cc == 1) { 59 | run("Image Sequence... ", "dir=" + dir + " format=TIFF start=0 digits=4"); 60 | } else { 61 | if(ss > 1) { 62 | for(i = 0; i < ss; i++) { 63 | selectWindow(win); 64 | run("Duplicate...", "duplicate slices=" + (i + 1)); 65 | saveAs("Tiff", dir + "mmTemp" + IJ.pad(i, 4) + ".tif"); 66 | close(); 67 | } 68 | } else { 69 | for(i = 0; i < ff; i++) { 70 | selectWindow(win); 71 | run("Duplicate...", "duplicate frames=" + (i + 1)); 72 | saveAs("Tiff", dir + "mmTemp" + IJ.pad(i, 4) + ".tif"); 73 | close(); 74 | } 75 | } 76 | } 77 | selectWindow(win); 78 | close(); 79 | setBatchMode(false); 80 | } -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Make_Montage_Directory.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | The aim is to make montages the way we like them. 3 | Horizontal - grayscale single channels with merge(s) on the right 4 | Vertical - grayscale single channels with merge(s) on the bottom 5 | 6 | The code got a spring clean in Feb 2020 which allows more channels for the grayscale 7 | and other colours in the merge(s). 8 | 9 | Notes: 10 | 1. row or column montages are generated (user decides) 11 | 2. Input can be 8-bit, 16-bit stacks/composites or single slice RGB Images 12 | 3. Specify the grouting of the montage (white space between panels) 13 | 4. There's no outside border, and there is an option to add a scale bar (scaling taken from image) 14 | 5. The idea is to compile them afterwards using Montage Compiler 15 | 16 | This is to batch process a directory of TIFFs. 17 | */ 18 | 19 | 20 | macro "Make Montages Directory" { 21 | if (nImages > 0) exit ("Please close all open images"); 22 | dir1 = getDirectory("Source Directory "); 23 | dir2 = getDirectory("Destination Directory "); 24 | 25 | montageMakerMulti(dir1, dir2, true, "none"); 26 | } 27 | -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/Montage_Compiler.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * Montage compiler will compile montages to make a figure. 3 | * Previously the user needed to determine the orientation 4 | * Now the function determines the orientation in order to: 5 | * 1) array row montages vertically, or 6 | * 2) array column montages horizontally 7 | */ 8 | 9 | macro "Montage Compiler" { 10 | if (nImages < 2) exit ("2 or more images are required"); 11 | mtgcomp(); 12 | } 13 | -------------------------------------------------------------------------------- /scripts/LabCode/Figure Maker/ROI_Zoom.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * This macro picks an ROI and makes an expanded version in the corner 3 | * The ROI and the expansion have a white border 4 | * User can select the size of the ROI, the expansion, the corner and the border size 5 | * It is meant to be used for montages but it will work on single square images. 6 | * Open 1 image. Run Macro. Pick settings. 7 | * Click in the centre of where you want your ROI to be (any panel will work). 8 | * There is an option to use foreground color instead of white (default) for borders. 9 | * http://github.com/quantixed/imagej-macros/ 10 | */ 11 | 12 | macro "Add ROI Zoom" { 13 | if (nImages > 1) exit ("Use a single image or single montage"); 14 | if (nImages == 0) exit("No image open"); 15 | 16 | imageID = getImageID(); 17 | title = getTitle(); 18 | dir1 = getDirectory("image"); 19 | newName = "zooms_" + title; 20 | run("Select None"); 21 | getDimensions(w, h, c, numSlices, nFrames); 22 | // code doesn't work as intended with muli-channel images 23 | if (c > 1) exit("Image must be RGB or single channel"); 24 | // deal with multiple frames or slices 25 | nShots = maxOf(numSlices,nFrames); 26 | if (numSlices > 1 && nFrames > 1) exit("Number of slices and frames is greater than one"); 27 | if (numSlices == 1) sliceOrFrame = 0; 28 | if (numSlices > 1) sliceOrFrame = 1; 29 | if (nFrames > 1) sliceOrFrame = 2; 30 | 31 | // work out grout size 32 | if (w == h) { 33 | nCol = 1; 34 | nRow = 1; 35 | grout = 0; 36 | nPanel = 1; 37 | vChoice = ""; 38 | } else if (w > h) { 39 | nCol = floor(w/h); 40 | nRow = 1; 41 | grout = (w - (nCol * h)) / (nCol - 1); 42 | if (nCol == 1) { 43 | grout = 0; 44 | } 45 | nPanel = nCol; 46 | vChoice = ""; 47 | } else { 48 | nCol = 1; 49 | nRow = floor(h/w); 50 | grout = (h - (nRow * w)) / (nRow - 1); 51 | if (nRow == 1) { 52 | grout = 0; 53 | } 54 | nPanel = nRow; 55 | vChoice = "vert"; 56 | } 57 | // ask what size ROI and expansion and corner 58 | cornerArray = newArray("LT", "RT", "LB", "RB"); 59 | labels = newArray(nPanel); 60 | defaults = newArray(nPanel); 61 | panelDecisions = newArray(nPanel); 62 | for (i=0; i h) && lengthOf(vChoice) == 0) exit("Zoom will be too big, use different expansion"); 94 | if ((dSize > w) && vChoice == "vert") exit("Zoom will be too big, use different expansion"); 95 | // maybe they want to make the zoom the width of panel, let them but limit stroke so it doesn't go into next panel 96 | if ((dSize == h && bStroke > grout) && lengthOf(vChoice) == 0) exit("Use different stroke size to do this"); 97 | if ((dSize == w && bStroke > grout) && vChoice == "vert") exit("Use different stroke size to do this"); 98 | // entered a silly number 99 | if (bStroke > 20 * grout && grout > 0) exit("Use a smaller stroke size"); 100 | 101 | // User defines the centre of the box for expansion 102 | setTool(7); // not sure how to force single point vs multi-point 103 | waitForUser("Define box", "Click on the image to centre the box for expansion.\n\nTo change position, either drag the point,\nor click again and last point will be used"); 104 | if (selectionType == 10) { 105 | getBoundingRect(xp, yp, width, height); 106 | // print("x="+xp+" y="+yp+" "+width+" "+height); 107 | makeRectangle(xp-(bSize/2),yp-(bSize/2),bSize,bSize); 108 | selectImage(imageID); 109 | } else exit("Works with point selection only"); 110 | 111 | // figure out which panel the selection is in 112 | // each panel is h x h pixels separated by grout for horizontal 113 | // and w x w pixels separated by grout for vertical 114 | // this is risky but box should not be less than grout away from panel edge 115 | // will fail in cases of large grout or many panels. 116 | // sp is the panel where selection is, 0-based 117 | if (vChoice == "vert") { 118 | sp = floor(yp / w); 119 | // x and y coords of ROI centre relative to the panel LT 120 | xp1 = xp - 0; // --for future dev 121 | yp1 = yp - (sp * (w + grout)); 122 | // sanity check in case user has clicked too close to the edge 123 | if (xp1-(bSize/2) < 0 || yp1-(bSize/2) < 0) exit("Try again, too close to the edge"); 124 | if (xp1+(bSize/2) > w || yp1+(bSize/2) > w) exit("Try again, too close to the edge"); 125 | } else { 126 | sp = floor(xp / h); 127 | // x and y coords of ROI centre relative to the panel LT 128 | xp1 = xp - (sp * (h + grout)); 129 | yp1 = yp - 0; // --for future dev 130 | // sanity check in case user has clicked too close to the edge 131 | if (xp1-(bSize/2) < 0 || yp1-(bSize/2) < 0) exit("Try again, too close to the edge"); 132 | if (xp1+(bSize/2) > h || yp1+(bSize/2) > h) exit("Try again, too close to the edge"); 133 | } 134 | 135 | setBatchMode(true); 136 | // border will be white if colorChoice is True 137 | if (colorChoice) { 138 | if (bitDepth() == 8) setColor(255); 139 | if (bitDepth() == 24) setColor(255,255,255); 140 | if (bitDepth() == 16) setColor(65535); 141 | } else { 142 | setColor(getValue("color.foreground")); 143 | } 144 | 145 | if (vChoice == "vert") { 146 | if (corner == "RT" || corner == "RB") { 147 | dStartx = w - dSize; 148 | } 149 | else if (corner == "LT" || corner == "LB") { 150 | dStartx = 0; 151 | } 152 | // do the copy/pasting 153 | for (i=0; i 1) exit ("Use a single image or single montage"); 13 | if (nImages == 0) exit("No image open"); 14 | 15 | imageID = getImageID(); 16 | title = getTitle(); 17 | dir1 = getDirectory("image"); 18 | newName = "zooms_" + title; 19 | run("Select None"); 20 | getDimensions(w, h, c, numSlices, nFrames); 21 | // code doesn't work as intended with muli-channel images 22 | if (c > 1) exit("Image must be RGB or single channel"); 23 | // deal with multiple frames or slices 24 | nShots = maxOf(numSlices,nFrames); 25 | if (numSlices > 1 && nFrames > 1) exit("Number of slices and frames is greater than one"); 26 | if (nShots > 1) exit("Stacks are not currently supported"); 27 | 28 | // work out grout size 29 | if (w == h) { 30 | nCol = 1; 31 | nRow = 1; 32 | grout = 0; 33 | nPanel = 1; 34 | vChoice = ""; 35 | } else if (w > h) { 36 | nCol = floor(w/h); 37 | nRow = 1; 38 | grout = (w - (nCol * h)) / (nCol - 1); 39 | if (nCol == 1) { 40 | grout = 0; 41 | } 42 | nPanel = nCol; 43 | vChoice = ""; 44 | } else { 45 | nCol = 1; 46 | nRow = floor(h/w); 47 | grout = (h - (nRow * w)) / (nRow - 1); 48 | if (nRow == 1) { 49 | grout = 0; 50 | } 51 | nPanel = nRow; 52 | vChoice = "vert"; 53 | } 54 | // ask what size ROI and expansion 55 | labels = newArray(nPanel); 56 | defaults = newArray(nPanel); 57 | panelDecisions = newArray(nPanel); 58 | for (i=0; i h) && lengthOf(vChoice) == 0) exit("Box will be too big, use different expansion"); 94 | if ((bSize > w) && vChoice == "vert") exit("Box will be too big, use different expansion"); 95 | 96 | // User defines the centre of the box for expansion 97 | setTool(7); // not sure how to force single point vs multi-point 98 | waitForUser("Define box", "Click on the image to centre the box for expansion.\n\nTo change position, either drag the point,\nor click again and last point will be used"); 99 | if (selectionType == 10) { 100 | getBoundingRect(xp, yp, width, height); 101 | // print("x="+xp+" y="+yp+" "+width+" "+height); 102 | makeRectangle(xp-(bSize/2),yp-(bSize/2),bSize,bSize); 103 | selectImage(imageID); 104 | } else exit("Works with point selection only"); 105 | 106 | // figure out which panel the selection is in 107 | // each panel is h x h pixels separated by grout for horizontal 108 | // and w x w pixels separated by grout for vertical 109 | // this is risky but box should not be less than grout away from panel edge 110 | // will fail in cases of large grout or many panels. 111 | // sp is the panel where selection is, 0-based 112 | if (vChoice == "vert") { 113 | sp = floor(yp / w); 114 | // x and y coords of ROI centre relative to the panel LT 115 | xp1 = xp - 0; // --for future dev 116 | yp1 = yp - (sp * (w + grout)); 117 | // sanity check in case user has clicked too close to the edge 118 | if (xp1-(bSize/2) < 0 || yp1-(bSize/2) < 0) exit("Try again, too close to the edge"); 119 | if (xp1+(bSize/2) > w || yp1+(bSize/2) > w) exit("Try again, too close to the edge"); 120 | } else { 121 | sp = floor(xp / h); 122 | // x and y coords of ROI centre relative to the panel LT 123 | xp1 = xp - (sp * (h + grout)); 124 | yp1 = yp - 0; // --for future dev 125 | // sanity check in case user has clicked too close to the edge 126 | if (xp1-(bSize/2) < 0 || yp1-(bSize/2) < 0) exit("Try again, too close to the edge"); 127 | if (xp1+(bSize/2) > h || yp1+(bSize/2) > h) exit("Try again, too close to the edge"); 128 | } 129 | 130 | setBatchMode(true); 131 | // border will be white if colorChoice is True 132 | if (colorChoice) { 133 | if (bitDepth() == 8) setColor(255); 134 | if (bitDepth() == 24) setColor(255,255,255); 135 | if (bitDepth() == 16) setColor(65535); 136 | } else { 137 | setColor(getValue("color.foreground")); 138 | } 139 | 140 | filestr = ""; 141 | // do the copy/pasting 142 | for (i=0; i 1) { 25 | imgType = "stack"; 26 | } else { 27 | imgType = "hyperstack"; 28 | } 29 | 30 | // find the text file containing time stamps and load it. 31 | pathfile = File.openDialog("Choose the file to Open:"); 32 | list = File.openAsString(pathfile); // opens file 33 | entries = split(list, "\n"); // splits lines of text into array 34 | 35 | // warn user if the number of timestamps is less than the number of slices or 36 | if(imgType == "stack" && (entries.length != slices)) print("Warning: number of timestamps does not match number of slices"); 37 | if(imgType == "hyperstack" && (entries.length != frames)) print("Warning: number of timestamps does not match number of frames"); 38 | 39 | stampSize = howBigShouldTextBe(width,height); 40 | // make dialog for user input 41 | Dialog.create("Timestamp options"); 42 | cornerArray = newArray("LT", "RT", "LB", "RB"); 43 | Dialog.addChoice("Corner for timestamps", cornerArray); 44 | Dialog.addNumber("Size", stampSize); 45 | Dialog.addCheckbox("Flatten?", false); 46 | Dialog.show(); 47 | 48 | // Collect data from dialog window 49 | stampPos = Dialog.getChoice(); 50 | stampSize = Dialog.getNumber(); 51 | flattenOpt = Dialog.getCheckbox(); 52 | 53 | setBatchMode(true); 54 | run("Colors...", "foreground=white background=black selection=white"); 55 | selectWindow(title1); 56 | polarity = false; 57 | for (i = 0; i < entries.length; i++) { 58 | j = parseInt(entries[i]); 59 | if (j < 0) { 60 | polarity = true; 61 | break; 62 | } 63 | } 64 | 65 | for (i = 0; i < entries.length; i++) { 66 | t = parseInt(entries[i]); // this line results in seconds or minutes being whole numbers 67 | if (polarity == true) { 68 | if (t>=0) s = "+"+pad(floor((t/60)%60))+":"+pad(t%60); // time sec/60 stuff 69 | else s = "-"+pad(floor((-t/60)%60))+":"+pad(-t%60); // deal with negative number 70 | } else { 71 | s = ""+pad(floor((t/60)%60))+":"+pad(t%60); // time sec/60 stuff 72 | } 73 | // this shouldn't work for hyperstacks but it does - suspect this may break in the future 74 | setSlice(i + 1); // select slice number, 1-based 75 | setMetadata("Label", s); 76 | } 77 | // setTool("text"); 78 | stampArray = whereDoesStampGo(stampPos, width, height, stampSize, polarity); 79 | run("Label...", "format=Label x="+stampArray[0]+" y="+stampArray[1]+" font="+stampSize+" use"); 80 | if (flattenOpt == true) { 81 | run("Flatten"); 82 | } 83 | setBatchMode(false); 84 | } 85 | 86 | function pad(n) { 87 | // pad single digit numbers with a preceding zero 88 | str = toString(n); // decimal rep of number j 89 | if (lengthOf(str)==1) str="0"+str; 90 | 91 | return str; 92 | } 93 | 94 | function howBigShouldTextBe(ww, hh) { 95 | // check if image is an unsual aspect ratio, if so use small size 96 | if((ww / hh < 0.25) || (hh / ww < 0.25)) { 97 | return 8; 98 | } else { 99 | bigDim = maxOf(ww,hh); 100 | fSize = floor(bigDim / 200) * 12; 101 | } 102 | 103 | return fSize; 104 | } 105 | 106 | function whereDoesStampGo(corner, ww, hh, textSize, longLabel) { 107 | coords = newArray(2); 108 | if (longLabel == true) { 109 | xSize = 4 + (3.5 * textSize); 110 | ySize = -4.5 + (0.9 * textSize); 111 | } else { 112 | xSize = 4 + (2.75 * textSize); 113 | ySize = -4.5 + (0.9 * textSize); 114 | } 115 | 116 | if (corner == "LT") { 117 | coords[0] = 2; 118 | coords[1] = 2 + ySize; 119 | } 120 | if (corner == "LB") { 121 | coords[0] = 2; 122 | coords[1] = hh - 2; 123 | } 124 | if (corner == "RT") { 125 | coords[0] = ww - 2 - xSize; 126 | coords[1] = 2 + ySize; 127 | } 128 | if (corner == "RB") { 129 | coords[0] = ww - 2 - xSize; 130 | coords[1] = hh - 2; 131 | } 132 | 133 | return coords; 134 | } 135 | -------------------------------------------------------------------------------- /scripts/LabCode/Movie Annotation/Save_Time_Stamps_To_Text_File.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * This macro was originally contributed by Meghane Sittewelle 3 | * Allows user to save text file of time stamps for use in the main macro 4 | * 230714 - macro now works with files with multiple series 5 | * This change means that there is now a header in the text file to specify 6 | * Series, Z, C, T (all 0-index) and time (in units not specified). 7 | * To use the timestamps in the main macro a single column of times is required 8 | */ 9 | 10 | macro Save_Time_Stamps_To_Text_File { 11 | run("Bio-Formats Macro Extensions"); 12 | id = File.openDialog("Choose a file"); 13 | Ext.setId(id); 14 | // get number of series and total images in each series 15 | Ext.getSeriesCount(seriesCount); 16 | Ext.getImageCount(imageCount); 17 | // find total number of timings 18 | totalCount = seriesCount * imageCount; 19 | 20 | // make a text file 21 | dirsave = File.getDirectory(id); 22 | filename = File.getNameWithoutExtension(id); 23 | f = File.open(dirsave+File.separator+filename+"_deltaT.txt"); 24 | print(f, "series" + "," + "z" + "," + "c" + "," + "t" + "," + "deltaT" + "\n"); 25 | 26 | for (so = 0; so < seriesCount; so ++) { 27 | if(seriesCount > 1) { 28 | Ext.setSeries(so); 29 | } 30 | for (no = 0; no < imageCount; no ++) { 31 | Ext.getZCTCoords(no, z, c, t); 32 | Ext.getPlaneTimingDeltaT(deltaT, no); 33 | print(f, so + "," + z + "," + c + "," + t + "," + deltaT + "\n"); 34 | } 35 | } 36 | 37 | File.close(f); 38 | } 39 | -------------------------------------------------------------------------------- /scripts/LabCode/Movie Annotation/Save_Time_Stamps_To_Text_Files_Directory.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * A wrapper to process all files in a directory (subdirectories are not included) and 3 | * extract the timing information from each file. The output is a text file with the 4 | * timing information for each image in each series. The output file is saved in the 5 | * output directory with the same name as the input file but with a "_deltaT.txt" 6 | */ 7 | 8 | #@ File (label = "Input directory", style = "directory") input 9 | #@ File (label = "Output directory", style = "directory") output 10 | #@ String (label = "File suffix", value = ".tif") suffix 11 | 12 | processFolder(input); 13 | 14 | // function to scan folders/subfolders/files to find files with correct suffix 15 | function processFolder(input) { 16 | list = getFileList(input); 17 | list = Array.sort(list); 18 | for (i = 0; i < list.length; i++) { 19 | if(!File.isDirectory(input + File.separator + list[i]) & endsWith(list[i], suffix)) 20 | processFile(input, output, list[i]); 21 | } 22 | } 23 | 24 | function processFile(input, output, file) { 25 | run("Bio-Formats Macro Extensions"); 26 | id = input + File.separator + file; 27 | Ext.setId(id); 28 | // get number of series and total images in each series 29 | Ext.getSeriesCount(seriesCount); 30 | Ext.getImageCount(imageCount); 31 | // find total number of timings 32 | totalCount = seriesCount * imageCount; 33 | 34 | // make a text file 35 | filename = File.getNameWithoutExtension(file); 36 | f = File.open(output+File.separator+filename+"_deltaT.txt"); 37 | print(f, "series" + "," + "z" + "," + "c" + "," + "t" + "," + "deltaT" + "\n"); 38 | 39 | for (so = 0; so < seriesCount; so ++) { 40 | if(seriesCount > 1) { 41 | Ext.setSeries(so); 42 | } 43 | for (no = 0; no < imageCount; no ++) { 44 | Ext.getZCTCoords(no, z, c, t); 45 | Ext.getPlaneTimingDeltaT(deltaT, no); 46 | print(f, so + "," + z + "," + c + "," + t + "," + deltaT + "\n"); 47 | } 48 | } 49 | 50 | File.close(f); 51 | } -------------------------------------------------------------------------------- /scripts/LabCode/qUtils/Make_NIS_Pure_RGB.ijm: -------------------------------------------------------------------------------- 1 | /* Nikon's NIS software allows channels to be saved with a LUT based on wavelength 2 | * This causes a problem because there is contamination of other channels under some circumstances. 3 | * This macro will convert the first three channels to pure RGB LUTs and save a version 4 | * Open all files first with BioFormats (AutoScale) Composite Hyperstack 5 | * Evaluates which channel is bluish, redish and greenish. 6 | * Note: an earlier version of this macro saved the file in 24-bit RGB BioFormats 7 | * this version retains the image type. 8 | */ 9 | 10 | setBatchMode(true); 11 | imgArray = newArray(nImages); 12 | dir1 = getDirectory("Choose Destination Directory "); 13 | for (i=0; i greens[255] && reds[255] > blues[255]) 26 | run("Red"); 27 | else if (greens[255] > reds[255] && greens[255] > blues[255]) 28 | run("Green"); 29 | else if (blues[255] > reds[255] && blues[255] > greens[255]) 30 | run("Blue"); 31 | else 32 | run("Grays"); 33 | } 34 | path = dir1 + win; 35 | if(!endsWith(path, ".tif")) { 36 | path = path + ".tif"; 37 | } 38 | saveAs("TIFF", path); 39 | close(); 40 | } 41 | -------------------------------------------------------------------------------- /scripts/LabCode/qUtils/Print_Titles_Of_Nice_Images.ijm: -------------------------------------------------------------------------------- 1 | /* 2 | * Idea is to open a whole bunch of images and leave the ones open that you like 3 | * This macro will print to the log the title of the nice images. 4 | */ 5 | 6 | macro "What nice images do I have?" { 7 | if (nImages < 1) { 8 | print("No images open"); 9 | return; 10 | } 11 | nameArray = newArray(nImages); 12 | print("\\Clear"); 13 | for (i=0; i