In a previous post (Digital images and arrays : the same thing ?) i moaned about the lack of support of negative coordinate in image representations. In order to go beyond a simple observation, and because i need it, here is my firsts lines (of code) on this subject.
First, what are the constraints? I write my programs in Clojure. Why? Because it is a Lisp and i love Lisps. Then because it is a efficient and dynamic language, running on the jvm. This last point is important because my programs need to be portable. In ordre to be really portable, my image representation needs to be usable by a java developper. Thus, I must write a class. But as i dislike Java (the language), this class will be written in Clojure. This is a good exercise of interoperability.
Definitions
From a very generalized point of view, an image is nothing else but a bounded two-dimensional function. The imfun class will be a child of clojure.lang.IFn, the interface representing functions in Clojure. This class contains an invoke method called when an instance is used as a function. It is only needed as internal state for each instance : a definition domain and a function. Here is the beginning of Imfun:
(ns tip.Imfun (:gen-class :implements [clojure.lang.IFn] :state state :init init :methods [[fun [] clojure.lang.IFn] [domain [] clojure.lang.IPersistentCollection]] :constructors {[clojure.lang.IPersistentCollection clojure.lang.IFn] []}))Some details:
- the first line gives the used namespace:
Imfun, :gen-classallows creation of*.classfiles,- this class implements the
clojure.lang.IFninterface, - the internal state is store in
state, - the initialisation of an instance is realized with the
initfunction, - two methods are exposed :
funanddomain, - the constructor arguments are : a collection (for the definition domain) and a function.
The init function must returns a vector with : 1/ the arguments to be passed to the parent constructor (nothing here), and 2/ the current instance state. the init arguments must comply the ones defined with :constructor.
(defn -init [dom f] [[] [dom f]])The two methods
fun and domain must returns the underlying function and the definition domain, all stored in state.
(defn -domain [this] (first (.state this))) (defn -fun [this] (second (.state this)))The
invoke method is called when the instance is applied as a function. At this moment, the coordinates must be checked to be inside the definition domain (thanks to indom? function). If the coordinates are outside, the value 0 is returned (this will be changed in the future).
(defn indom? [dom pt] (let [[start end] dom [x y] pt] (and (>= x (first start)) (<= x (first end)) (>= y (second start)) (<= y (second end)))))(defn -invoke ([this x y] (if (indom? (.domain this) [x y]) ((.fun this) x y) 0)) ([this pt] (let [[x y] pt] (this x y))))
Files organisation and compilation
If my working directory is work, two sub-dirs must be created: src and classes. The generated *.class files will go in classes. The arborescence in src must be conforms to the namespace. Thus the previous lines of code should be put in a file work/src/tip/Imfun.clj. The compilation is then realized with:
fredm:work> java -cp clojure.jar:./src:./classes clojure.main -e "(compile 'tip.Imfun)"
(this step can be realized in the clojure repl (read-eval-print-loop))
Use
Open a clojure repl :
fredm:work> java -cp clojure.jar:./src:./classes clojure.main -e Clojure 1.1.0-alpha-SNAPSHOT user=> (import '(tip Imfun)) tip.ImfunLet's create an image
im, being defined from (-1, -1) to (1, 1) and returning the sum of the coordinates values:
fredm:work> java -cp clojure.jar:./src:./classes clojure.main -e Clojure 1.1.0-alpha-SNAPSHOT user=> (def f #(+ %1 %2))'user/f
user=> (def im (Imfun. [[-1 -1] [1 1]] f))
'user/im
im is a bounded function, returning 0 outside of its definition domain.
user=> (.domain im) [[-1 -1] [1 1]] user=> (im -1 -1) -2 user=> (im 0 1) 1 user=> (im 1 2) 0It is even possible to apply
im on two coordinates x and y, or to a collection of two elements containing the coordinates (i.e. a point):
user=> (im 1 1) 2 user=> (im [1 1]) 2 user=> (im '(1 1)) 2Here it is. Next time a second class will be written, heriting
Imfun in order to define images with values stores in a two-dimensional array. In fact, an ImageProcessor from ImageJ library will be used.
FMN
- Une sélection (automatique) de billets similaires :
- Plugin ImageJ minimal en Clojure: inverser une image
- Représentation d'images (Clojure et Java) #2
- Utiliser ImageJ dans un notebook Sage, un exemple d'appel de Java depuis Python
- Image numérique et tableau : est-ce identique ?
- Fonction avec méta-données (Clojure). Représentation d'images #3
[...] fois j’avais montré comment représenter une image en tant qu’une fonction bornée (voir ce billet). La classe java était intégralement écrite en Clojure. Cette représentation correspond à la [...]