MIN-Fakultät
Fachbereich Informatik
Szenenanalyse und Visualisierung (SAV)

Bildverarbeitung mit Allegro Common Lisp und der VIGRA = VIGRACL

Aufbau der Schnittstelle

Da es mit Allegro Common Lisp oder auch mit Ansi Commmon Lisp recht leicht möglich ist, sogenannten "Foreign-Code", also Code der nicht mit Lisp selbst geschrieben wurde zu laden, wird diese Schnittstelle (das Foreign-Funktion-Interface: FFI) im Rahmen der VIGRACL ausgenutzt. Diese ermöglicht es, C-konforme Shared Libraries (unter Windows: DLLs, unter MacOSX: dylibs) dynamisch zur Laufzeit an Lisp zu binden, so dass die enthaltenen Funktionen benutzt werden können.
Da die VIGRA allerings nur für Import- bzw. Export von Bildern eine Shared Library anbietet, musste als erste Zwischenstufe ein C-Wrapper für die VIGRA-Funktionen geschrieben werden (VIGRA_C), die innerhalb der Allegro Common Lisp Umgebung zur Verfügung stehen sollen. An diese dynamische C-Biblitohek werden dann die entsprechenden Common Lisp Funktionen gebunden, die den Aufruf vor dem Benutzer verschatten.

Verwendete Datenstrukturen

Eine weitere zentrale Frage ist die Wahl der Repräsentation eines Bilders innerhalb der Lisp-Umgebung. Dabei fiel die Wahl auf eine Liste, deren Elemente vom Common-Lisp Datentyp Array (und zwar in seiner zweidimensionalen Ausprägung) sind, wobei die erste Dimension von [0...Breite-1] und die zweite Dimension von [0...Höhe-1] reicht. Jedes Array enthält Werte vom Typ 'float' und steht für ein spezifisches Band. Die Bänder sind wie folgt angeornet: '(R G B) für Farbbilder und '(G) für Grauwertbilder
Die folgende Tabelle zeigt noch einmal übersichtlich alle Funktionsnamen, die diese Schnittstelle Common Lisp über die VIGRA_C bereitstellt. Dabei ist zu beachten, dass die '-band'-Funktionen jeweils auf ein Band eines Bildes angewendet werden können, wohin gegen die anderen Funktionen direkt auf obiger Bildrepräsentation arbeiten.

vigracl_logo

vigracl.impex

vigracl.imgproc

vigracl.filters

vigracl.segmentation

vigracl.morphology

loadimage

saveimage

resizeimage-band

resizeimage

rotateimage-band

rotateimage

affinewarpimage-band

affinewarpimage

reflectimage-band

reflectimage

fouriertransform-band

fouriertransform

convolveimage-band

convolveimage

separableconvolveimage-band

separableconvolveimage

gsmooth-band

gsmooth

gaussiangradient-band

gaussiangradient

ggradient-band

ggradient

laplacianofgaussian-band

laplacianofgaussian

gsharpening-band

gsharpening

sharpening-band

sharpening

nonlineardiffusion-band

nonlineardiffusion

distancetransform-band

distancetransform

labelimage-band

labelimage

watersheds-band

watersheds

cannyedgeimage-band

cannyedgeimage

differenceofexponentialedgeimage-band

differenceofexponentialedgeimage

regionimagetocrackedgeimage-band

regionimagetocrackedgeimage

erodeimage-band

erodeimage

dilateimage-band

dilateimage

openingimage-band = (dilateimage-band (erodeimage-band band))

openingimage = (dilateimage (erodeimage img))

closingimage-band = (erodeimage-band (dilateimage-band band))

closingimage = (erodeimage (dilateimage img))

Außerdem gibt es noch in CommonLisp geschriebene Hilfsfunktionen, die den Umgang mit Bildern erleichern in der funktionalen Programmierung erleichern sollen:

vigracl.helpers

:array-map

array-map!

array-reduce

array-for-each-index

image-map

image-map!

image-reduce

image-for-each-index-array

Anwendungsbeispiele

Einbindung der VIGRACL in Allegro Common Lisp (dafür muss dem Common Lisp der Pfad bekannt sein, in dem die VIGRACL liegt):

CL-USER> (load "vigracl.cl")

Anschließend kann man zum Beispiel ein beliebiges Bild laden (hier: "blox.gif"):

CL-USER> (setq img (vigracl.impex:loadimage  "blox.gif"))

Seit Version 0.3.0 kann man auch direkt in das VIGRACL-Paket wechseln und hier drin ohne Paket-Präfix der Funktionen weiterarbeiten:
Ab hier wird davon ausgegangen, dass es sich um Versionen >= 0.3.0 handelt. Andernfalls müssten die Paketnamen wieder als Präfix vor die Funktionen geschrieben werden

CL-USER> (in-package :vigracl)
#<The VIGRACL package>
VIGRACL> (setq img (loadimage  "blox.gif"))

Das Bild mit einem Gaussfilter mit sigma=3 glätten und das geglättete Bild in img2 ablegen:

VIGRACL> (setq img2 (gsmooth img 3))

Das geglättete Bild in den diskretisierten Frequenzraum überführen:

VIGRACL> (setq img3 (fouriertransform img2))

Die Wasserscheiden-Transformation auf das Gradientenbetragsbild (Gauss'scher Gradientenfilter, sigma=1) des vegrößerten Ausgangsbildes (vergr. auf 500x500 Pixel) anwenden:

VIGRACL> (setq img4  (watersheds
                          (ggradient 
                              (resizeimage img 500 500 4)
                                  1.0)))

Eigene Bildverarbeitungsfunktionen implementieren:
Ein Schwellenwertbild bilden (Pixelwerte >50 : 1, 0 sonst) und dessen zusammenhängende Regionen durchnumerieren:

VIGRACL> (defun threshold-band (arr t)  
             (let* ((width  (array-dimension arr 0))
      		        (height (array-dimension arr 1))
                    (arr2   (make-array (array-dimensions arr)
                                  :element-type (array-element-type arr)
                                  :initial-element 0.0))
                     (foo     (do* ((j 1 (+ j 1)))
                                   ((= j (- (array-dimension arr 1) 1)))
                                   (do* ((i 1 (+ i 1)))
                                        ((= i (- (array-dimension arr 0) 1)))
                                           (if (> (aref arr i j) t)
                                               (setf (aref arr2 i j) 1.0)
                                               (setf (aref arr2 i j) 0.0))))))
                     arr2))
VIGRACL> (compile 'threshold)

VIGRACL> (setq img5  (labelimage
				(mapcar #'(lambda (arr) (threshold-band arr 50)) img)))

ACHTUNG: Bei eigenen Funktionen nicht das (compile 'function_name) vergessen! Dies führt zu einer deutlichen Steigerung der Ausführungsgeschwindigkeit

Mit den neuen Array-Hilfsfunktionen (seit Version 0.2.0) lässt sich das obige Beispiel auch wie folgt schreiben:

VIGRACL> (defun threshold (arr t)  
                (image-map #'(lamba (x) (if (> x t) 1.0 0.0))
                           arr))
VIGRACL> (compile 'threshold)

An diesem Beispiel kann man sehr gut die funktionale Abstraktionsebene sehen, die sich durch die neuen array-Hilfsfunktionen ergibt. Sie macht die Programme lesbarer und eleganter.

Download & Installation

Das aktuelle Paket (Version 0.5.0) kann hier heruntergeladen werden. Es beinhaltet im Ordner vigracl die VIGRACL Anbindung und im Unterordner vigra_c die nötigen VIGRA_C Dateien (siehe oben).Dieses Paket beinhaltet nicht die VIGRA! Diese muss bereits auf dem System installiert sein.

Veränderungen gegenüber Version 0.4.0:

  • Einführung von Farbbildern
  • Definition der VIGRACL mithilfe Allegros defsystem
  • Interface-änderung: Bilder werden nicht mehr als array repräsentiert, sonden als Listen von arrays
  • Neue Funktionen: Die alten Funktionen, die auf Arrays arbeiten besitzen nun die Endung '-band'. Die neuen gleichnamigen Funktionen arbeiten nun auf der neuen Bildrepräsentation (s.o)
  • Neue Funktionen: convolveimage[-band] und separableconvolveimage[-band] zur Faltung von Bildern
  • Neue Funktion: gaussiangradient[-band] Ermittelt die partiellen Ableitungen in x- und y-Richtung, anstatt (vgl. mit ggradient[-band]) nur die Stärke zu bestimmen.
  • Die Beispieldateien wurden auf Farbbilder angepasst.

Die alte Version (Version 0.4.0) kann hier heruntergeladen werden. Es beinhaltet im Ordner vigracl die VIGRACL Anbindung und im Unterordner vigra_c die nötigen VIGRA_C Dateien (siehe oben).Dieses Paket beinhaltet nicht die VIGRA! Diese muss bereits auf dem System installiert sein.

Veränderungen gegenüber Version 0.3.0:

  • Weitere Array-Hilfsfunktionen wurden eingeführt: array-map!, array-reduce und array-for-each-index.
  • Interface-änderung: array-map und array-map! verwenden jetzt die Signatur (func arrays) statt (arr func) und lassen auch nicht unäre Funktionen auf Listen von Arrays zu.
  • Interface-änderung: array-map und array-map! arbeiten nun auf Listen. Daher wurde array-combine abgeschafft!
  • Interface-änderung: array-copy wurde in copy-array umbenannt.
  • Interface-änderung: Die Fourier Transformation beinhaltet nicht mehr die Skalierung des Bildes. Die neue Signatur ist einfach (arr) statt (arr new_width new_height)
  • Neue Funktion: affinewarpimage mit Signatur (arr affinematrix resize_mode), zur affinen Transformation eines Bildes. AffineMatrix muss hierbei ein 3x3 Array vom Typ 'double-float sein.
  • Neu: Morphologische Funktionen erodeimage(arr), dilateimage(arr), openingimage(arr) und closingimage(arr) wurden eingeführt
  • Neue Funktion: regionimagetocrackedgeimage(arr) wurde eingeführt
  • Die Beispieldateien ("examples.cl" und "fft_example.cl") wurden auf die neuen Funktionen bzw. Funktionssignaturen umgestellt.

Die alte Version (Version 0.3.0) kann hier weiterhin heruntergeladen werden.

Veränderungen gegenüber Version 0.2.0:

  • Einheitliche Paketumgebung wurde eingeführt.
  • Die Beispieldateien ("examples.cl" und "fft_example.cl") wurden auf das neue Paketsystem umgestellt.

Die alte Version (Version 0.2.0) kann hier weiterhin heruntergeladen werden.

Veränderungen gegenüber Version 0.1.0:

  • Fehler in der Fast Fourier-Transformation wurde behoben.
  • array-Hilfsfunktionen wurden eingeführt:
    array-copy (Kopieren von Arrays)
    array-map (Anwenden von Funktionen auf alle Elemente im Array)
    array-combine (Kombination zweier Arrays mittels einer binären Funktion)
  • Weitere Beispieldatei wurde hinzugefügt : Umgang mit den array-Funktionen sowie mit der FFT ("fft_example.cl")
  • Allgemeine Beispieldatei wurde umbenannt von "example.cl" in "examples.cl"

Die alte Version (Version 0.1.0) kann hier weiterhin heruntergeladen werden, wovon ich allerdings wegen der behobenen Fehler dringed abrate

Installationshinweise für MacOS X:

Erstellen der dynamischen Bibliothek (vigra_c.dylib)
Dazu muss das Makefile im Unterordner vigra_c ausgeführt werden. Nach erfolgreichem Aufruf sollte sich eine Datei namens "vigra_c.dylib" im VIGRACL-Verzeichnis befinden.

Installationshinweise für Linux:

Erstellen der dynamischen Bibliothek (vigra_c.so)
Das Erstellen einer Linux-Bibliothek wird zur Zeit noch nicht direkt unterstützt, es sollte jedoch kein Problem sein die Anpassungen zu tun, die dafür nötig sind:

  1. Das Makefile im Ordner "vigracl/vigra_c" so anpassen, dass ein C-kompatibles shared object file im Ordner vigracl erzeugt wird.
  2. Die vigracl.cl Datei musss nun nur noch so angepasst werden, dass sie auch nach dieser Datei sucht, falls das Betriebssystem ein Linux ist. Dies müsste in Zeile 2-4 ergänzt werden.

Installationshinweise für Windows:

Erstellen der dynamischen Bibliothek (vigra_c.dll)
Dazu befindet sich im Unterordner "vigra_c\win32-msvc2005" ein Visual Studio Projekt. Hier müssen eventuell noch die Pfade, die zur VIGRA führen, angepasst werden. Danach kann man mit "Projekt erstellen" die DLL erzeugen. Nach erfolgreichem Aufruf sollte sich eine Datei namens "vigra_c.dll" im Release-Verzeichnis befinden. Diese muss nun noch ins "vigracl" Verzeichnis kopiert werden

Weitere Meilensteine:

  • Einbindung weiterer VIGRA-Funktionen