Difference between revisions of "Clojure Scripting"
(Moved fibonacci to appendix)
(Added shallow and deep sequences over native arrays)
|Line 430:||Line 430:|
(0 1 1 2 3 5 8 13 21 34)
(0 1 1 2 3 5 8 13 21 34)
Revision as of 15:45, 28 October 2008
- 1 Clojure tutorial for ImageJ
- 1.1 Language basics
- 1.2 Calling methods and variables on a java object
- 1.3 Calling static fields and methods: namespace syntax
- 1.4 Defining variables: obtaining the current image
- 1.5 Creating objects: invoking constructors
- 1.6 Defining a closure
- 1.7 Looping an array of pixels
- 1.8 Executing commands from the menus
- 1.9 Creating and using Clojure scripts as ImageJ plugins
- 1.10 Using java beans for quick and convenient access to an object's fields
- 2 Examples
- 3 Appendix
- 3.1 Defining the output stream
- 3.2 Namespaces
- 3.3 JVM arguments
- 3.4 Reflection
- 3.5 Lambda functions
- 3.6 Built-in documentation
- 3.7 A fibonacci sequence: lazy and infinite sequences
- 3.8 Creating shallow and deep sequences from java arrays
Clojure tutorial for ImageJ
Clojure is not a scripting language: Clojure compiles directly to JVM bytecode, and thus runs at native speed. Thus one must think of Clojure as a true alternative to Java the language, but much more expressive, flexible and powerful.
A ';' defines the start of a comment, just like '//' does in Java.
A function definition declares parameters within .
Local variables are declared with let, and global variables with def.
Functions are defined with defn, and are visible globally. Hence a function declared within a let statement has access to variables declared in it. This method enables closures.
Calling methods and variables on a java object
There are two ways, the second syntactic sugar of the first. Below, imp is a pointer to an ImagePlus:
; java-ish way: (. imp (getProcessor)) ; lisp-ish way: (.getProcessor imp)
To call a method on an object returned by a method call, there is a simplified way:
; double way: (. (. imp (getProcessor)) (getPixels)) ; simplified double way: (.. imp (getProcessor) (getPixels)) ; or lisp-ish way: (.getPixels (.getProcessor imp))
To call a variable or 'field', just do it like a method call but without the parentheses:
(. imp changes)
or more lisp-ish:
To enhance readability, use import when appropriate. Imports remain visible throughout the current namespace:
(import '(java.awt Color Rectangle) '(ij.plugin.filter PlugInFilter)) (new Rectangle 0 0 500 500) ; It's the same as: (Rectangle. 0 0 500 500) (. PlugInFilter DOES_ALL) ; or since it's a static field, call like a namespace: (PlugInFilter/DOES_ALL)
Choose whatever matches your mental schemes best.
Calling static fields and methods: namespace syntax
To call a static field or method, use namespace syntax:
(ij.plugin.filter.PlugInFilter/DOES_ALL) (ij.IJ/log "Some logged text")
Above, notice how a class name is used instead of a pointer to call static fields and methods. Static fields and methods are just variables and functions that exist within the namespace of the class in which they are declared. Hence Clojure's namespace syntax makes way more sense than java code, that doesn't do such distinction and allows for loads of confusion (java allows invoking static methods and fields using a pointer to an instance of the class in which such static methods and fields are declared).
Defining variables: obtaining the current image
As a local variable imp declared within a let statement:
(let [imp (ij.WindowManager/getCurrentImage)] ; print its name (println (.getTitle imp))) ; Variable imp NOT visible from outside let statement: (println (.getTitle imp)) ---> ERROR
As a general variable visible from the entire namespace:
(def *imp* (ij.WindowManager/getCurrentImage)) (println (.getTitle *imp*))
By convention, in lisp global variables are written with asterisks in the name.
A let statement lets you declare any number of paired variable name / values, even referring to each other in sequence:
(let [imp (ij.WindowManager/getCurrentImage) ip (.getProcessor imp) pix (.getPixels ip) pix2 (.getPixels (.getProcessor imp))] ; do some processing ... (println (str "number of pixels: " (count pix))))
Any number of let statements may be nested together:
(let [imp (ij.WindowManager/getCurrentImage)] ; do whatever processing here (let [ip (.getProcessor imp] ; print first pixel (println (str (.getPixel ip 0 0))))))
Creating objects: invoking constructors
A constructor is invoked by adding a dot '.' to the name of a class, followed by the arguments. Below, we create an ImageProcessor and then an ImagePlus with it, and finally we print the ImagePlus, which invokes toString() on it (like in java):
(let [ip (ij.process.ByteProcessor. 400 400) imp (ij.ImagePlus. "my image" ip)] (println imp))
An alternative syntax is to use the java-like new keyword, but it's unnecessarily verbose:
(let [ip (new ij.process.ByteProcessor 400 400) imp (new ij.ImagePlus "my image" ip)] (println imp))
Defining a closure
In the following a function is declared within the scope of the local variable rand, which contains an instance of java.util.Random. All calls to the function rand-double will use the same random number generator instance with seed 69997.
The dotimes loop will then print 10 different pseudo-random numbers. If the rand was a new Random with seed 69997 every time, all 10 numbers would be exactly the same.
You can think of a function inside a closure as a static function using a static variable (in Java), but it's more than that, since the function will be able to access parameters on the global namespace and also in any other local namespace in which the let is declared. For example, another let, or even another defn!
(let [rand (java.util.Random. 69997)] (defn rand-double  (.nextDouble rand))) (dotimes i 10 (println (rand-double)))
Above, note the dot '.' after Random, which indicates we are calling the constructor (with a single parameter 69997, the pseudorandom generator seed to be used). Alternatively, one may use the java-like syntax: (new java.util.Random 69997) -- note the absence of a dot now.
Looping an array of pixels
For example, to find the min and max values:
; Obtain the pixels array from the current image (let [imp (ij.WindowManager/getCurrentImage) pixels (.. imp (getProcessor) (getPixels)) min (apply min pixels) max (apply max pixels)] (println (str "min: " min ", max: " max)))
The above code does not explicitly loop the pixels: it simply applies a function to an array.
To loop pixels one by one, use any of the following:
(let [imp (ij.WindowManager/getCurrentImage) pixels (.. imp (getProcessor) (getPixels))] ; First loop with "dotimes" (dotimes i (count pixels) (println (aget pixels i))) ; Second loop: with "loop -- recur" (loop [i 0 len (count pixels)] (if (< i len) (let [pix (aget pixels i)] (println (str i ": " pix)) (recur (inc i) len))))
Above, notice that the loop -- recur construct is essentially a let declaration, with a second call (recur) to reset the variables to something else. In this case, the next index in the array. Note how the len is simply given the same value over and over, just to avoid calling (count pixels) at each iteration.
Of course, there are lispier ways to loop an array of pixels. For example, to obtain the average of all pixels, we can use function reduce, which takes the first two elements of a list, applies a function to them, and then applies the function to the result and the next element, etc:
(let [fp (.getProcessor (ij.IJ/getImage)) pix (.getPixels fp)] (if (instance? ij.process.FloatProcessor fp) (println "Average pixel intensity" (/ (reduce + pix) (count pix))) (println "Not a 32-bit image")))
Above, notice that one could have used also apply to apply + to all element of an array, with the same result:
(println "Average pixel intensity" (/ (apply + pix) (count pix)))
To sum all pixels in an 8-bit image, one needs first to bit-and all bytes to 255, so they become integers and can be summed. But of course we should not bit-and the sum! To solve this, reduce accepts a first value (in this case, zero):
(let [bp (.getProcessor (ij.IJ/getImage)) pix (.getPixels bp)] (if (instance? ij.process.ByteProcessor bp) (println "Average intensity: " (float (/ (reduce (fn [x1 x2] (+ x1 (bit-and x2 255))) 0 pix) (count pix)))) (println "Not an 8-bit image")))
It could even be done using a local variable, but it's ugly and undesirable (why create it when it's not really needed)? Notice we need to create the local variable "sum" because variables declared by let are immutable.
(let [bp (.getProcessor (ij.IJ/getImage)) pix (.getPixels bp)] (if (instance? ij.process.ByteProcessor bp) (with-local-vars (sum 0) (doseq pixel pix (var-set sum (+ (var-get sum) (bit-and pixel 255)))) (println (float (/ (var-get sum) (count pix))))) (println "Not an 8-bit image")))
Any ImageJ menu command may be run on the active image:
(ij.IJ/doCommand "Add Noise")
Be aware that the above starts a new Thread and forks. For reliable control, try the run method, which will wait until the plugin finishes execution.
(ij.IJ/run "Add Noise")
Creating and using Clojure scripts as ImageJ plugins
Simply create a text file with the script inside, and place it in the plugins menu or any subfolder. Then call Plugins - Scripting - Refresh Clojure Scripts to make it appear on the menus.
If the macros/StartupMacros.txt includes a call to the Refresh Clojure Scripts inside the AutoRun macro, then all your Clojure scripts will appear automatically on startup.
To modify an script which exists already as a menu item, simply edit its file and run it by selecting it from the menus. No compiling necessary, and no need to call Refresh Clojure Scripts either (ther latter only for new scripts or at start up.)
Very important: all scripts and commands from the interpreter will run within the same thread, and within the same clojure context.
Using java beans for quick and convenient access to an object's fields
Essentially it's all about using get methods in a trivial way. For example:
(let [imp (ij.WindowManager/getCurrentImage) b (bean imp)] (println (:title b) (:width b) (:height b)))
Eventually Clojure may add support for set methods as well.
Fixing overexposed images: setting a pixel value to a desirable one for all overexposed pixels
The problem: Leginon or the Gatan TEM camera acquired an overexposed image, and set all pixels beyond range to zero.
The solution: iterate all pixels; if the pixel is zero then set it to a desirable value, such as the maximum value of the main curve in the histogram (push 'auto' on the Brightness and Contrast dialog to see it.)
In the example below, the fix function is called with the current image and the value 32500 as a floating point number. Notice also the type definition (which is optional) of the float pixel array, to enhance execution speed:
; Assumes a FloatProcessor image (defn fix [imp max] (let [#^floats pix (.getPixels (.getProcessor imp))] (loop [i (int 0)] (if (< i (alength pix)) (do (if (= 0 (aget pix i)) (aset pix i (float max))) (recur (inc i))))))) (let [imp (ij.IJ/getImage)] (fix imp (float 32500)) (.updateAndDraw imp))
Creating a script for ImageJ
Simply write the clojure script in a text file, and follow these conventions:
1. Add an underscore "_" to its file name, and the extension ".clj": fix_leginon_images.clj 2. Save it under fiji/plugins/ folder, or a subfolder.
When done, just run the "PlugIns/Scripting/Refresh Clojure Scripts" plugin.
Once saved and in the menus, you need not call refresh scripts ever again for that script. Just edit and save it's text file, and run it again from the menus. Next time Fiji opens, the script will automatically appear in the menus.
See Scripting Help for more details, including how to use the built-in dynamic interpreter.
Example Clojure plugins included in Fiji
Open the plugins/Examples/ folder in Fiji installation directory. You'll find three Clojure examples:
- Multithreaded_Image_Processing.clj: illustrate, with macros (via defmacro), how to automatically multithread the processing of an image using arbitrary subdivision of the image, such as one line per thread, for as many threads as cores the CPU has.
- blend_two_images.clj: illustrates how to open two images from an URL, and blend the gray image into each channel of the color image.
- celsius_to_fahrenheit.clj: illustrates the usage of a Swing GUI, and how to instantiate anonymous classes from an interface (via proxy Clojure function). This example is taken from the Clojure website.
- random_noise_example.clj: illustrates how to declare a function inside a closure (for private access to, in this case, a unique instance of a random number generator), and then fill all pixels of a ByteProcessor image with a random byte value.
Defining the output stream
The default output stream is at variable *out*. The Clojure Interpreter binds it to the PrintWriter that prints to the interpreter text area, by executing any typed in code within the following binding:
(binding [*out* (Clojure.Clojure_Interpreter/getStdOut)] ; any typed input here )
- To list all existing namespaces:
>>> (all-ns) (#<Namespace: xml> #<Namespace: zip> #<Namespace: clojure> #<Namespace: set> #<Namespace: user>)
- To list all functions and variables of a specific namespace, first get the namespace object by name:
(ns-map (find-ns 'xml))
Note above the quoted string "xml" to avoid evaluating it to a (non-existing) value.
- To list all functions and variables of all namespaces:
(map ns-map (all-ns))
- To get the arguments passed to the JVM, see contents of variable *command-line-args*
- To list all methods of an object:
(defn print-java-methods [obj] (doseq method (seq (.getMethods (if (= (class obj) java.lang.Class) (identity obj) (class obj)))) (println method))) ; Inspect an object named imp, perhaps an image (print-java-methods imp) public synchronized boolean ij.ImagePlus.lock() public void ij.ImagePlus.setProperty(java.lang.String,java.lang.Object) public java.lang.Object ij.ImagePlus.getProperty(java.lang.String) ...
- To list constructors, just use .getConstructors instead of .getMethods.
(Thanks to Craig McDaniel for posting the above function to Clojure's mailing list.)
- To declare functions on the fly, lambda style, with regex for arguments:
For example, declare a function that takes 2 arguments, and returns the value of the first argument divided by 10, and multiplied by the second argument:
(let [doer #(* (/ %1 10) %2)] (doer 3 2))
Of course there's no need to name the function, the above is just for illustration.
Mapping a function to all elements in a list
- To declare a nameless function, and apply it to each element of a list:
In this case, increment by one each value of a list from 0 to 9:
(let [numbers (range 10) add-one (fn [x] (+ x 1))] (map add-one numbers))
There is no need to declare the names, the above is just for illustration. Above, we could have defined the function as #(+ %1 1):
(map #(+ %1 1) (range 10))
... or of course use the internal function inc which does exactly that:
(map inc (range 4))
Beware that the map function above applies the given function to each element of a list, and returns a new list with the results.
Use the function doc to query any other function. For example, the list generator function range:
(doc range) ------------------------- clojure/range ([end] [start end] [start end step]) Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step, where start defaults to 0 and step to 1.
Above, notice the function has three groups of possible arguments, denoted in brackets.
A fibonacci sequence: lazy and infinite sequences
A beautiful example of using lazy sequences and applying functions to one or more sequences at a time.
Below, the sequence fibs is defined in such a way that it contains all possible fibonacci numbers. Since such sequence is infinite, we declared it as lazy sequence, that creates new elements only when they are asked for.
The lazy-cat clojure function creates such lazy sequence by concatenation of two sequences: the first sequence is 0, 1 (which takes the role of feeder or initialization sequence), and the second sequence is the result of a map operation over two subsets of the fibs sequence itself: the full and the full minus the first element (hence the rest operation to obtain the list of all elements without the first).
A map operation applies a function to each element of a sequence, or, when two or more sequences are provided, to the corresponding elements: those at index 0 in all sequences, those at index 1 in all sequences, etc.
To generate the fibonacci sequence of numbers, a sum + operation is mapped to the numbers contained in the self sequence fibs and to the corresponding elements of the self sequence fibs minus the first element, i.e. shifted by one.
In short, the lazy sequence fibs is an abstract way of representing a potentially infinite sequence, with an implementation containing a full abstract definition of all fibonacci numbers.
Then we just take the first 10 elements of such lazy sequence, which are created on the fly.
(def fibs (lazy-cat [0 1] (map + fibs (rest fibs)))) (take 10 fibs)
(0 1 1 2 3 5 8 13 21 34)
Creating shallow and deep sequences from java arrays
Many clojure functions take sequences, not native java arrays, as arguments. A java native array can be wrapped by a shallow sequence like the following:
>>> (def pixels (into-array (range 10))) #'user/pixels >>> pixels [Ljava.lang.Integer;@f30d8e >>> (def seq-pix (seq pixels)) #'user/seq-pix >>> seq-pix (0 1 2 3 4 5 6 7 8 9)
Now if we modify the native array, the sequence will reflect that change too when read:
>>> (aset pixels 3 99) 99 >>> seq-pix (0 1 2 99 4 5 6 7 8 9)
The array was not duplicated. The only new object created was the shallow sequence:
>>> (class seq-pix) clojure.lang.ArraySeq
To create a true deep duplicate of the array, one can do:
>>> (def pixels2 (vec pixels)) #'user/pixels2 >>> (class pixels2) clojure.lang.LazilyPersistentVector >>> pixels2 [0 1 2 99 4 5 6 7 8 9] >>> (def seq-pix2 (seq pixels2)) #'user/seq-pix2 >>> (class seq-pix2) clojure.lang.APersistentVector$Seq
Or, in short:
(def seq-pix2 (seq (vec pixels))) #'user/seq-pix2
So now any changes to the original pixels array will not affect the new sequence:
>>> (aset pixels 3 101) 101 >>> seq-pix2 (0 1 2 99 4 5 6 7 8 9)
Thanks to Chouser and wwmorgan for examples on #clojure at irc.freenode.net