Difference between revisions of "Clojure Scripting"

m (Added category)
Line 139: Line 139:
 
</pre>                                                                                                                                                                                                                 
 
</pre>                                                                                                                                                                                                                 
 
<p>Above, notice that the <i>loop -- recur</i> construct is essentially a <i>let</i> declaration, with a second call (<i>recur</i>) 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 <i>(count pixels)</i> at each iteration.</p>
 
<p>Above, notice that the <i>loop -- recur</i> construct is essentially a <i>let</i> declaration, with a second call (<i>recur</i>) 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 <i>(count pixels)</i> at each iteration.</p>
                                                                                                                                                                                                                     
+
 
 +
<p>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 <i>reduce</i>, 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:</p>
 +
 
 +
<pre>
 +
(let [fp (.getProcessor (.getImage ij.IJ))
 +
      pix (.getPixels fp)]
 +
  (if (instance? ij.process.FloatProcessor fp)
 +
      (println "Average pixel intensity" (/ (reduce + pix) (count pix)))
 +
      (println "Not a 32-bit image")))
 +
</pre>
 +
 
 +
<p>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 <i>bit-and</i> the sum! To solve this, <i>reduce</i> accepts a first value (in this case, zero):</p>
 +
 
 +
<pre>
 +
(let [bp (.getProcessor (.getImage ij.IJ))
 +
      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")))
 +
</pre>
 +
 
 
<h3>Executing commands from the menus</h3>                                                                                                                                                                             
 
<h3>Executing commands from the menus</h3>                                                                                                                                                                             
 
<p>Any ImageJ menu command may be run on the active image:</p>                                                                                                                                                         
 
<p>Any ImageJ menu command may be run on the active image:</p>                                                                                                                                                         

Revision as of 12:31, 22 May 2008

Clojure tutorial for ImageJ

Check out clojure web site and particularly the chapter on Java interoperability.

Language basics

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)

(. ij.plugin.filter.PlugInFilter DOES_ALL)

Above, notice how a class name is used instead of a pointer to call static fields.

To enhance readability, use import when appropriate. Imports remain visibile throughout the current namespace:

(import '(java.awt Color Rectangle)
        '(ij.plugin.filter PlugInFilter))

(new Rectangle 0 0 500 500)

(. PlugInFilter DOES_ALL)

Choose whatever matches your mental schemes best.


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 (. imp (getTitle))))

; Variable imp NOT visible from outside let statement:
(println (. imp (getTitle)))
---> ERROR

As a general variable visible from the entire namespace:

(def *imp* (. ij.WindowManager (getCurrentImage)))

(println (. *imp* (getTitle)))

; Alternative syntax
(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 (. imp (getProcessor))
      pix (. ip (getPixels))
      pix2 (.. imp (getProcessor) (getPixels))]
    ; 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 (. imp (getProcessor)]
        ; print first pixel
	(println (str (. ip (getPixel 0 0)))))))


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 (new java.util.Random 69997)]
    (defn rand-double []
        (. rand (nextDouble))))

(dotimes i 10
    (println (rand-double)))

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 (.getCurrentImage ij.WindowManager)
      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 (.getImage ij.IJ))
      pix (.getPixels fp)]
   (if (instance? ij.process.FloatProcessor fp)
      (println "Average pixel intensity" (/ (reduce + pix) (count pix)))
      (println "Not a 32-bit image")))

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 (.getImage ij.IJ))
      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")))

Executing commands from the menus

Any ImageJ menu command may be run on the active image:

                                                                                                                                                                                                                 
(.doCommand ij.IJ "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.

(.run ij.IJ "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.

Appendix

  • 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* (. getStdOut Clojure.Clojure_Interpreter)]
   ; 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*