Subscribe RSS

Image representation (Clojure and Java) #2

October 14th, 2009 by fmn | Filed under Enseignement, Research .

Last time was given an image representation as a bounded function (see this post). This java class was completely written in Clojure. This representation is the equivament of an image from a mathematical view. It is not very efficient. Usually an image is stored as a 2-d array. This data structure allows short access time to the pixel values.

So, in java with ImageJ, an image is an abstract class named ImageProcessor. Four concretes implementation are proposed : ByteProcessor, ShortProcessor, FloatProcessor et ColorProcessor (see api). As previously discussed on this blog, the drawback of these kind of representation is the difficulty to handle negative negatives coordinates.

This post is thus devoted to an image representation based on an ImageProcessor, but allowing negatives coordinates. The proposed class Image is heriting from previous class Imfun.


Initialisation

(ns tip.Image
  (:gen-class
   :extends tip.Imfun
   :state state
   :init init
   :methods [[ip [] ij.process.ImageProcessor]
             [set [Integer Integer Object] void]]
   :constructors {[clojure.lang.IPersistentCollection ij.process.ImageProcessor]
                  [clojure.lang.IPersistentCollection clojure.lang.IFn]}))

(defn -init [start ip]
  (let [[xs ys] start
        dom [start [(dec (+ xs (.getWidth ip)))
                    (dec (+ ys (.getHeight ip)))]]]
    [[dom #(.get ip (- %1 xs) (- %2 ys))]
     [dom ip]]))

Internally, the image is stored in state as as a domain definition (dom) and an ImageProcessor (ip). The init method compute the definition domain from the origin coordinates (start) and the image dimensions, taken from the ImageProcessor. The init function returns a vector with the arguments to be passed to the parent constructor and the current instance state. As the super class is Imfun, the first part of the returned vector contains an access function to the ImageProcessor pixels.

ImageProcessor access

(defn -ip [this]
  (second (.state this)))

Nothing special except that in the super class, the second element of state contains a function while here it is the ImageProcessor. This way, the reference to the super constructor:

:constructors {[clojure.lang.IPersistentCollection ij.process.ImageProcessor]
               [clojure.lang.IPersistentCollection clojure.lang.IFn]}

is useless as the intern function does not exists. Moreover, in the super class Imfun the fun function allows to get the intern function and thus the access to the pixel values:

(defn -fun [this]
  (second (.state this)))

It is thus essential to modifiy the fun function in order to take advantage of the intern ImageProcessor.


Pixel access

The access is provided with the fun method. In order to get the pixel value from an ImageProcessor, several methods are proposed by ImageJ : getting an int with get and getPixel or getting a float with getf et getPixelValue. The get and getf are fastest as no bound checkning is realized.

In order to have only one access function fun (called by invoke), a polymorphic function is defined:

(defmulti -fun (fn [this] (class (.ip this))))

The dispatch is realized on the concrete class of the ImageProcessor.

For a FloatProcessor, the getf method is prefered:

(defmethod -fun ij.process.FloatProcessor [this]
  (let [[xs ys] (first (.domain this))
        fp (.ip this)]
    #(.getf fp (- %1 xs) (- %2 ys))))

For a ByteProcessor, the get method returns a byte contained in an int. Only the lowest 8 bits must be selected:

(defmethod -fun ij.process.ByteProcessor [this]
  (let [[xs ys] (first (.domain this))
        bp (.ip this)]
    #(bit-and (byte (.get bp %1 %2)) 255)))

The same for a ShortProcessor, only the lowest 16 bits are kept:

(defmethod -fun ij.process.ShortProcessor [this]
  (let [[xs ys] (first (.domain this))
        sp (.ip this)]
    #(bit-and (short (.get sp %1 %2)) 65535)))

The return value of get for a ColorProcessor is an int containing 3 bytes (one for each color component)

(defmethod -fun ij.process.ColorProcessor [this]
  (let [[xs ys] (first (.domain this))
        cp (.ip this)]
    #(.get cp %1 %2)))


Pixel modification

In the same spirit as the fun method, the set method is a multimethod:

(defmulti -set (fn [this x y v] (class (.ip this))))

(defmethod -set ij.process.FloatProcessor [this x y v]
  (let [[xs ys] (first (.domain this))]
        (.setf (.ip this) (- x xs) (- y ys) v)))

(defmethod -set :default [this x y v]
  (let [[xs ys] (first (.domain this))]
    (.set (.ip this) (- x xs) (- y ys) v)))


Discussion

This image representation is completed. It is possible to manipulate an image as a bounded function or as a 2-d array. These two alternatives allow images with negative coordinates.

The drawback is the mutable data structure ImageProcessor. The Clojure way is to not modify state (as possible) but to provide a modified copy of the state. The canon is to use pure functions. The Clojure api provides immutable sequences.

Next post will be devoted to a trial of an image representation based on these immutable sequences.

FMN.

Tags: , , , , ,

Leave a Reply