BAR
BAR (ImageJ/Fiji) | |
---|---|
Maintainer | Tiago Ferreira |
Source | on GitHub |
Initial release | June 2014 |
Latest version | 1.1.13 March 2017 (Changelog) |
Category | Analysis, Annotation, Filtering, Segmentation, Scripting |
BAR: A collection of Broadly Applicable Routines.
The collection contains Macros, Scripts and Plugins focused on Data Analysis, Image Annotation and Image Segmentation. It is curated using GitHub and distributed as an optional update site.
Installation
Run Help › Update... and choose Manage update sites. Activate the BAR checkbox in the alphabetically-sorted list of update sites. Press OK, then Apply changes. Restart ImageJ. That's it. Enjoy BAR!
Description
BAR files are accessible through a dedicated top-level menu subdivided in task-oriented categories. All routines should be documented on GitHub.
Some of the scripts have a dedicated documentation page, others feature built-in help, while a handful were deemed too simple to require dedicated instructions. Nevertheless, all files contain useful commentary at the top of the source code file. Remember: You can open all the scripts using the Shift key.
List of BARs
- Analysis
- LoG-DoG Spot Counter, Multi ROI Profiler, Multichannel Plot Profile, Multichannel ZT-axis Profile, Smoothed Plot Profile
- Data Analysis
- Create Boxplot, Create Polar Plot, Distribution Plotter, Find Peaks, Fit Polynomial, Interactive Plotting
- Segmentation
- Shen-Castan Edge Detector, Apply Threshold To ROI, Clear Thresholded Pixels, Remove Isolated Pixels, Threshold From Background, Wipe Background
- Snippets, BAR lib and Tutorials
- Described in Scripting BARs
- Tools and Toolsets
- Calibration Menu, List Folder Menu, Segment Profile, Shortcuts Menu, ROI Manager Tools, Toolset Creator
Accessing BARs
As with all ImageJ commands, BAR scripts can be accessed in multiple ways: 1) through the BAR▷ menu, 2) the Context Menu, 3) Keyboard Shortcuts, 3) the Shortcuts Menu Tool (BAR▷ Tool Installers▷ Install Shortcuts Menu), that registers frequently used commands in the ImageJ toolbar, 4) by pressing L, or 5) from other scripts, macros and plugins.
Context Menu
BAR▷ submenus can be appended to the image's context menu (the menu that pops up when right-clicking on the image canvas) by running BAR▷ Submenu▷Move Menu (Context<>Main). The transfer is bidirectional: once in the context menu, running the same command will place the submenu back in the main menu bar.
The shuttling mechanism is not permanent, i.e., it will not be remembered across restarts. However, it is macro recordable which means it can be imposed at startup, using the ImageJ macro language. So, e.g., to install BAR▷ Segmentation▷ in the context menu, one would:
- Start the Macro Recorder ( Plugins▷ Macros▷ Record...)
- Run BAR▷ Segmentation▷ Move Menu (Context<>Main)
- Open the Edit▷ Options▷ Startup... window and paste the string generated by the Macro Recorder into its text area so that ImageJ can run the command at every startup.
It may be wise to allow ImageJ enough time to register all scripts before triggering transfers to the context menu. This can be achieved through the built-in macro function wait(). For a slow setup requiring at least 1 second (1000 milliseconds), the pasted code would look something like this:
Notes
- The several Move Menu (Context<>Main) commands across BAR▷ submenus do not use the same label and are distinguishable by extra trailing spaces. This is intentional because all ImageJ commands must have unique names.
- Any toolset loaded via the ">>" More Tools drop down menu can define its own contextual menu (as detailed in the ImageJ User Guide, the contextual menu is controlled by a macro called Popup Menu that gets loaded at startup). To have BARs immediately available when such toolsets are loaded, just append the same run("Move Menu (Context<>Main)"); call described above for StartupMacros.
Commander
Since the majority of BARs are scripts stored in dedicated files, BAR features Commander (BAR › BAR Commander...), a keyboard-based file browser that produces filtered lists of directory contents.
It is a productivity tool that applies the principles of Command Launcher to file browsing, providing instant access to files just by typing abbreviations of filenames. It serves two purposes: 1) to expedite the opening of files and 2) to produce filtered lists of directory contents. Features include: drag-and-drop support, interaction with native file manager, regex filtering, and a built-in console for common operations.
Console mode is triggered by typing !, which evokes a list of searchable commands so that all file navigation can be done exclusively with the keyboard. Some of these (cd, ls, pwd, etc.) are reminiscent of commands found in most command-line interfaces. Here are some examples:
- To access ImageJ's LUT folder
- Type !+L+U+T+⌅ Enter
- To access all JavaScript lib files
- Type !+L+I+B+⌅ Enter, then .+J+S
- To reveal the directory of active image
- Type !+I+M+P+⌅ Enter, then choose Reveal Path.
- To access Commander's built-in help
- Type !+H+E+L+P+⌅ Enter
- To extract the paths of all TIFF images in a directory
- Drag and drop the desired folder into the Commander list
Type T+I+F+⌅ Enter
Choose Print Current List in the Options Menu or press ^ Control+P (⌘ Command+P in Mac OS).
Keyboard Shortcuts
You can use Plugins▷ Shortcuts▷ Create Shortcut... to assign hotkeys (e.g., keyboard key that you do not use frequently such as 0 or F7) to any script registered in the BAR▷ menu. These shortcuts will be listed in Plugins▷ Shortcuts▷ and are remembered across restarts.
Alternatively, keyboard shortcuts can be defined in macros that call BAR commands by placing the shortcut key within square brackets at the end of the macro name. Such macros can pass specific options to BAR commands, allowing scripts to run without prompt. Example:
macro "Remove Round Structures [0]" { run("Wipe Background", "size=100 circ.=0.75-1.00"); // Runs Wipe_Background.ijm with the specified parameters }
As mentioned, such macros can then be pasted into the text area of Edit▷ Options▷ Startup... so that they can be executed when ImageJ starts up.
Scripting BARs
Although BARs can be used as standalone commands, the scripts and plugins in BAR become more useful when incorporated into other routines.
You can use BARs as a starting point for your own workflows. Whether you are just looking to automate a simple task or you are an advanced developer, you can use BAR to achieve your analysis goals more easily, by means of Snippets - source code templates - and libs - scripting additions to be shared across routines.
Snippets
BAR contains a directory, plugins/Scripts/BAR/Snippets/, containing multi-language examples that you can customize and recycle in your own scripts. You can, of course, also retrieve code and inspiration from the more complete BARs in the remaining plugins/Scripts/BAR/ subdirectories. Any script or macro file stored in the Snippets/, folder with an underscore "_" in the filename will be listed in BAR▷ Snippets▷. The Snippets▷ menu contains some utilities to help you manage your scripts:
- List Snippets
- Prints a table listing all scripts in plugins/Scripts/BAR/Snippets/. Files can then be opened in the Script Editor by double-clicking on their filename.
- New Snippet
- A java plugin that speeds up the creation of new scripts, pre-configured to use BAR lib.
- Reveal Snippets
- Opens plugins/Scripts/BAR/Snippets/ in the file browser of the operating system.
- Search BAR
- Searches the contents of BAR files.
BAR lib
BAR libs (stored in the /BAR/lib/ directory) are centralized libraries (BeanShell, IJM and Python, etc.) that can be shared across files. These libraries serve as scripting additions to Snippets and other routines.
Do you find yourself copy and pasting functions from one file to the other? Do you keep on writing the same lines of code? Do you have some key code written across different languages? Would you like to make side-by-side comparisons of scripting languages? Then, BAR lib is for you.
The idea is quite simple: Reusable functions and methods are written to a lib file that gets loaded at execution time so that it can be called by the running script. BAR▷ Snippets▷ New Snippet... exemplifies how to use these scripting add-ons. Here is a BeanShell example:
// Add BAR/lib to classpath addClassPath(bar.Utils.getBARDir()); // See http://tferr.github.io/Scripts/apidocs/index.html?bar/Utils.html for details importCommands("lib/"); // Load BARlib.bsh BARlib(); // Confirm availability of BARlib lib = new BARlib(); lib.confirmLoading();
Run it in the Script Editor (File › New › Script...), and you should be greeted by a "BAR lib successfully loaded" message. Further details are provided on the GitHub lib page and on the documentation of the bar.Utils class.
Batch Processors
Some of the scripts included in /BAR/Snippets/ are scripts that apply a common operation to a directory. These batch processors are implemented in different languages and perform the following operations:
- Take an input folder specified by the user
- Apply a series of operations to individual files of matched extension(s)
- Save processed files as TIFF to a dedicated directory, so that no files are overwritten
Typically each of these tasks is handled by separated functions so only the function processing single files needs to be edited. In the Python and IJM implementation, this processing function is called myRoutines()
. Note that when editing myRoutines()
we do not need to worry about opening, closing or saving the image without overriding the original file, because those tasks are already performed by other functions.
Processing Functions
The file-processing function can include your own code, code generated by the Macro Recorder (Plugins▷ Macros▷ Record...), pre-existing snippets or methods/functions defined in a common BAR lib file.
IJM example, running a macro and a python script in the Snippets/ (another example below exemplifies how to call a macro function from BARlib.ijm):
function myRoutines() { snippetsPath = call("bar.Utils.getSnippetsDir"); runMacro(snippetsPath + "MyCoolestMacro.ijm"); eval("python", File.openAsString(snippetsPath + "Median_Filter.py")); }
Jython example, demonstrating how to 1) load BAR lib (in this case BARlib.py, using code generated by Bar▷ Snippets▷ New Snippet...) and 2) how to run a Snippet (in this case Median_Filter.py):
def myRoutines(): import sys, ij, bar # 1) Call a lib function: # 1.1) Extend the search path to /BAR/lib/ sys.path.append(bar.Utils.getLibDir()) # 1.2) Import all functions in /BAR/lib/BARlib.py import BARlib as lib # 1.3) Call a function from the file lib.confirmLoading() # 2) Run a script directly: from org.python.util import PythonInterpreter script = bar.Utils.getSnippetsDir() + "Median_Filter.py" PythonInterpreter().execfile(script)
Tip: Note also that there is an easier (but less flexible) way to batch process a folder: The built-in Process▷ Batch▷ Macro... command.
In this case, you only need to paste the contents of your myRoutines()
function into the text area of the command. However, by default, Process▷ Batch▷ Macro... assumes you want to process all the files in a directory. If that is not the case, i.e., you want to restrict the processing to certain file types, you will have to use regex to instruct the built-in command on the file extensions to be considered (see Commander's built-in help for several regex examples). E.g., typing the following in the File name contains field of Process▷ Batch▷ Macro..., would restrict processing to .tif, .stk and .oib files (the default extensions specified in the validExtension() function of Process_Folder_IJM.ijm):
(.*(\.(?i)(tif|stk|oib))$)
Example: Batch Randomization of Filenames
The default task of both the Python and IJM implementation of BAR Process Folder scripts is filename randomization: 1) They copy images from one folder to another, 2) Rename their filenames using a random string and 3) Log changes to a CSV table (so that id of randomized filename can be traced back to the original file). This approach allows for blind analyses of datasets that are sensitive to user interpretation. Below are the descriptions of Process_Folder_PY.py and Process_Folder_IJM.ijm.
Python
In Python we can use the uuid module to generate a random filename (rational here). The result is an impressively succinct function:
def myRoutines(image): import uuid image.setTitle( str(uuid.uuid4()) )
In more detail: Pass the active image - an
ImagePlus object - to myRoutines(). Retrieve a random UUID (e.g., f7dfd6a9-f745-42c2-8874-0af67380c3f5
), convert it to a string, then use that string to rename the image using the setTitle() method in ij.ImagePlus.
But because BAR libs already contain such a function, we can just call the randomString() function in BARlib.py, after loading the file:
def myRoutines(image): import sys, bar sys.path.append(bar.Utils.getLibDir()) import BARlib as lib image.setTitle( lib.randomString() )
To log filename changes, we could use the same strategy used for the IJM implementation. The simplest way to generate a CSV list would be to use ImageJ's Log window:
def myRoutines(image): import uuid from ij import IJ # Remember original filename before changing it log_row = image.getTitle() # Rename image image.setTitle( str(uuid.uuid4()) ) # Append modified filename to CSV row log_row += ", " + image.getTitle() # Print row IJ.log(log_row)
However, we can use the csv module to achieve a more robust implementation:
import csv, os # Create a CSV table documenting processed files csvPath = out_dir + "_ProcessedFileList.csv" csvExists = os.path.exists(csvPath) csvFile = open(csvPath, 'a') csvWriter = csv.writer(csvFile) # Specify column headings if not csvExists: headers = ['Original path','Processed path'] csvWriter.writerow(headers)
As such, we only need to add the following, every time a file is processed:
csvWriter.writerow([old_filename, new_filename])
Visit the BAR repository to check how the assembled script (Process_Folder_PY.py) looks like.
IJ Macro Language
In an ImageJ macro (IJM) we will need to define first a function that produces a random filename. The IJM language does not feature an equivalent to the UUID module used previously in the Python implementation. So, we are left with two approaches: 1) call java.util. randomUUID directly, or 2) write an ad-hoc function.
For the former, we take advantage of the IJM language built-in call() function, that calls public static methods in any Java class that ImageJ is aware of:
function myRoutines() { randomString = call("java.util.UUID.randomUUID"); rename(randomString); }
or even shorter:
rename(call("java.util.UUID.randomUUID"));
But discovering which methods can be called by the IJM language may not be trivial. Typically, it will require access to an IDE and some Java experience. So what about writing an ad-hoc function?
The approach used in Process_Folder_IJM.ijm is the following: 1) Take a template string containing the characters A-Z and digits 0-9; 2) Pick a random position between the first and last character of the string template. Extract the character at that position; 3) Repeat the last step several times, assembling extracted characters into a concatenated string:
function randomString() { // We define the template and measure the number of positions template = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; nChars = lengthOf(template); // We define an empty string that will hold the result string = ""; // We will want a final string to be 10-characters long for (i=0; i<10; i++) { // Define a random position between 0 and the penultime character in template idx = maxOf(0, round(random()*nChars-1)); // Extract and concatenate characters string += substring(template, idx, idx+1); } // return result return string; }
This would create e.g., NHH6KG30C9
. However, a lengthier filename may be required. Larger filenames would be harder to read, so we can insert hyphens at fixed positions. We will improve the function by passing two arguments to it: the length of the desired filename and a boolean flag to instruct on the usage of hyphens:
function randomString(length, spacers) { template = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; nChars = lengthOf(template); string = ""; for (i=0; i<length; i++) { idx = maxOf(0, round(random()*nChars-1)); string += substring(template, idx, idx+1); if (spacers && i%5==0) string += "_"; } return string; }
As such, calling randomString(50, true)
would produce e.g., E_ZXTQO_8E9XM_45WG7_8S39
. As with the Python implementation, we could also use BAR lib (in this case BARlib.ijm). First, we need to load the file before running our macro, using the code generated by Bar▷ Snippets▷ New Snippet:
libPath = call('bar.Utils.getLibDir') + 'BARlib.ijm'; libContents = File.openAsString(libPath); call('ij.macro.Interpreter.setAdditionalFunctions', libContents); // Press 'Run' twice to confirm availability of new additions confirmLoading();
Once lib/BARlib.ijm is in memory, the function can be called directly:
rename( randomString(50, true) );
Now that we are capable of generating a random filename, we just need to monitor filename changes. We could use the print() function (that outputs to the Log window), or create a two-column table describing the changes. Here is how the final Process_Folder_IJM.ijm looks like:
function myRoutines() { // Note that functions can contain other functions function randomString(length, spacers) {...} // Since we'll be logging filename changes to the Results table, we // need to keep track of the table's measurement counter. We'll assume // that the Results table was closed when myroutines() was first called availableRow = maxOf(0, nResults); // Log original filename before changing it setResult("Original filename", availableRow, getTitle()); // Rename image and log changes rename(randomString(20, true)); setResult("Randomized filename", availableRow, getTitle()); }
FAQ
- What is the rationale behind BAR?
- The motivation behind bar is quite simple: To collect snippets of code that can be incorporated into any workflow. So rather than performing fully fledged procedures, BARs tend to produce single tasks. The advantages are two-fold: 1) Scripts are easier to understand and maintain and 2) they can be used to complement any other ImageJ add-on, let it be the simplest macro or the most sophisticated plugin.
- Will I find BAR useful?
- Probably. But it is likely that you will need to delve a bit into the BAR philosophy.
- Can I contribute to BAR?
- Yes, please do! If you have some suggestions on how to improve it, do let us know.
- Nothing happens when I run a BAR. What's going on?
- In a case of premature termination BARs tend to exit rather silently. The best way to have insights on an unexpected error is to run it directly from the Script Editor: Open the script by holding ⇧ Shift while selecting it from the BAR▷ menu, press Run and have a look at the editors' s console, where all sort of useful messages will be printed to. Do let us know if you have found a bug.
- Does BAR work outside Fiji/ImageJ2?
- Yes, but with limitations. ImageJ1 (see ImageJ Flavors if you have doubts about existing ImageJ distributions) will only register scripts saved in the plugins/ folder or on one of its immediate subfolders. For this reason, some of the BAR submenus will appear as empty, and it may not be possible to navigate the BAR/ directory using menu commands (Commander could still be used, nevertheless). Another important aspect is that, without access to the built-in updater, you will have to manually update BAR (by monitoring its rpository), and to manually install (and update) the dependencies (i.e., third-party plugins and third-party libraries) used by BAR).
- How do I uninstall BAR?
- Run the Updater (Help › Update...). Choose Advance Mode then Manage update sites. Deactivate the BAR checkbox in the alphabetically-sorted list of update sites. Press OK, then Apply changes. All BAR files will be deleted. Note that you can install and uninstall BAR as you see fit. See How to follow a 3rd party update site for more details.
- I get an error when I try to load BAR lib (IJM). Why?
- Macro functions from a IJ macro lib may only be available once a new instance of the macro interpreter is initiated (this is not the case for other scripting languages). This means you have to call
ij.macro.Interpreter.setAdditionalFunctions
before running your macro. You can test this by running the default macro generated by BAR▷ Snippets▷New Snippet...:
// Load BARlib.ijm libPath = call('bar.Utils.getLibDir') + 'BARlib.ijm'; libContents = File.openAsString(libPath); call('ij.macro.Interpreter.setAdditionalFunctions', libContents);
- The first time you run
confirmLoading();
ImageJ will complain aboutconfirmLoading
being an undefined identifier, but it will successfully recognize the call the second time you run the code above.
Citation
BAR scripts can be cited using the DOI associated with the repository:
License
These scripts are free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.