Dieses Tutorial befindet sich noch im Aufbau.
Mehr zum Projekt auf https://github.com/luxeria/foip
====== Einführung in die Bildverarbeitung mit GNU Octave ======
In diesem Artikel sollen die grundlegenden Operationen der Bilderverarbeitung mit Octave zusammengefasst werden.
===== Installation =====
Die Installation von GNU Octave erfolgt bei Unixoiden Systemen über den
Paketmanager und kann direkt per Kommandozeile erfolgen. Hier das Beispiel
für den Paketmanager pacman von Archlinux
sudo pacman -S octave
und aptitude von Ubuntu (Debian)
sudo apt-get install octave
Für die Verwendung vom ''image'' Pakets muss Octave ab Version 4 installiert werden. Für Archlinux gibt es Octave ab Version 4 derzeit nur im [[https://aur.archlinux.org/packages/octave4/| AUR]]
==== Erweiterungen ====
Die Instalation von Erweiterungen von Octave kann direkt aus der
Kommandozeile von Octave erfolgen. Hierzu kann entweder das gewünschte
Softwarepaket manuell lokal heruntergeladen und danach installiert werden
oder direkt aus der Datenbank bezogen und installiert werden durch
Octave.
pkg install -forge image
Ist eine solche Erweiterung installiert wird diese nicht automatisch geladen
wenn eine neue Octave session eröffnet wird, sondern muss manuell geladen
werden.
pkg load image
Bei der Verwendung von vielen Erweiterungen können auch sämtliche mit der
Angabe ''all'' geladen werden.
pkg load all
===== Daten laden und speichern =====
Einstieg in jegliche Bildverarbeitung ist das Einlesen der Bilddaten und
auch deren Speicherung. Im einfachsten Fall kann der Prozess in drei Teile
gegliedert werden:
- Originaldatei einlesen
- Manipulation
- Manipulierte Daten ablegen
==== Graustufenbilder ====
Diese drei elementaren Schritte sind hier mit den beiden Funktionen
''imread()'' und ''imwrite()'' des image package ein
einem Pseudobeispiel dargestellt:
I = imread("input_image.png"); % read image
J = my_image_processing(I); % manipulate image
imwrite(J, "output_image.png"); % write image
Durch die Verwendung von ''imread()'' auf Graustufenbilder wird
die Grafik in eine entsprechende Matrix gespeichert mit den Pixelwerten.
Diese kann per ''imshow()'' betrachtet werden.
==== RGB Dateien ====
Beim Einlesen von Graustufenbildern sind die Pixelwerte direkt in einer
2D Matrix ableget. Beim Einlesen von RGB-Bildern wird die Grafik in eine
mehrschichtige 2D Matrix ableget, wobei jede Matrix die Pixelwerte für
eine Farbe enthält. Die einzelnen Matrizen können wiederum als
Graustufenbilder behandelt werden. Im Folgenden wird das Zerlegen eines
RGB-Bildes in die einzelnen Farblayer aufgezeigt.
Image = imread("input_rgb_image.png"); % read the RGB image
Red = Image(:,:,1); % extract red layer
Green = Image(:,:,2); % extract green layer
Blue = Image(:,:,3); % extract blue layer
Alternativ kann das GRB Bild auch direkt umgewandelt werden in ein Graustufenbild mittels ''rgb2gray()''.
ImageRGB = imread("image_rgb.png");
ImageGray = rgb2gray(ImageRGB);
imwrite(ImageGray, "image_gray.png");
==== Beispiel 1 ====
Das folgende [[https://github.com/luxeria/foip/blob/master/src/chapter_01/exercise_01.m| Beispiel 1]] liest eine RGB Bildatei ein und wandelt diese in ein Graustufenbild um. Die beiden Bilder werden in einem Plot gegenübergestellt und das neu erstellte Graustugenbild gespichert.
ImageOrig = imread(imageSource);
% convert the RGB image to a grayscale image
ImageGray = rgb2gray(ImageOrig);
% show the two images
figure(1);
subplot(1,2,1);
imshow(ImageOrig);
title("Original RGB Image");
subplot(1,2,2);
imshow(ImageGray);
title("Converted Grayscale Image");
% save the grayscale image
imwrite(ImageGray, "building_gray.jpg");
===== Pixel lesen und schreiben =====
Geladene Bilder sind in Octave einfache 2D Arrays bzw. Matrizen und können auch wie solche behandelt werden. Eine solche Matrix hat Spalten (x) und Zeilen (y) und wird als folgende Struktur dargestellt
Image = x1y1 x2y1 x3y1 x4y1 ... xny1
x1y2 x2y1 x3y2 x4y2 ... xny2
x1y3 x2y3 x3y3 x4y3 ... xny3
x1y4 x2y4 x3y4 x4y4 ... xny4
... ... ... ... ...
x1ym x2ym x3ym x4ym xnym
==== Beispiel 2 ====
Das [[https://github.com/luxeria/foip/blob/master/src/chapter_01/exercise_02.m| Beispiel 2]] zeigt wie Zeilen aus einem Bild gelesen werden. Diese Zeilen werden dann im einem Plot jeweils dargestellt.
% load the image
Image = imread(imageSource);
% get the size of the image
[sy, sx] = size(Image);
% define pixel lines to read from
redLine = floor(1*sy/10);
greenLine = floor(5*sy/10);
blueLine = floor(9*sy/10);
% extract pixel data
redPixels = Image(redLine,:);
greenPixels = Image(greenLine,:);
bluePixels = Image(blueLine,:);
===== Statistik =====
Für die Behandlung von Bilddaten sind statistische Auswertungen ein grundlegendes Werkzeug. Im folgenen sollen einige Grundbegriffe kurz erläutert werden.
==== Absolutes Auftreten ====
Nimmt man ein Graustufenbild und zählt für jeden Grauwert wie viele Pixel diesen Wert haben, dann erhält man damit das absolute Auftreten der Grauwerte. Mit diesem 'Zählen' erhält man eine Basis für weitere Auswertungen. Um ein Bild auf das absolute Auftreten von Grauwerten zu untersuchen gibt es die Funktion ''imhist()''.
[count, value] = imhist(Image);
==== Relative Häufigkeit ====
Die relative Häufigkeit beschreibt die Auftretenshäufigkeit eines Grauwertes in Prozent. Um die relative Häufigkeit zu erhalten wird das absolute Auftreten dividiert durch die Anzahl Pixel des Bildes. Mit dem Wert der relativen Häufigkeit kann also die Aussage gemacht werden, wie viel Prozent des Bildes genau diesen Grauwert haben.
[count, value] = imhist(Image);
relCount = count/numel(Image);
==== Kummulative Häufigkeit ====
Die kummulative Häufigkeit beschreibt den prozentualen Anteil der Pixel welche den Graustufenwert unterschreiten. Die y-Achse der kummulativen Häufigkeit ist also limitiert von 0 bis 1. Die Aussage welche gemacht werden kann ist, dass y% des Bildes einen Graustufenwert kleiner als x aufweist. Die Aussage kann auch umgedreht werden mit der Subtraktion von 1. Wenn also beispielsweise 30% der Pixel einen Graustufenwert kleiner als 128 haben, dann kann die Aussage getroffen werden, dass ''1 - 0.3 = 0.7 = 70%'' aller Pixel einen Wert höher als 128 haben.
Um die kummulative Häufigkeit eines Bildes zu erhalten muss die kummulative Summe berechnet werden der relativen Häufigkeit wie im Folgenden gezeigt wird
[count, value] = imhist(Image);
relCount = count/numel(Image);
cumCount = cumsum(relCount);
==== Mittelwert ====
Der (arithmetische) Mittelwert gibt den Wert an, der sich bei Summierung aller Werte dividiert duch die Anzahl Werte ergibt (sozusagen den Schwerpunkt).
In Octave kann der Mittelwert (engl. 'mean') eines Arrays bzw. Vecotrs mit der Funktion ''mean()'' ermittelt werden. Bilder sind jedoch ein 2D-Array bzw. eine Matrix und somit kommt abgeleitete Funktion ''mean2()'' zum Einsatz.
grayMean = mean2(Image);
==== Standardabweichung ====
Die Standardabweichung ist ein statistisches Mass für die Streuung. Es beschreibt eine mittlere Abweichung vom Mittelwert. Ein kleiner Wert bedeutet, dass viele Werte nahe beieinander liegen beim Mittelwert. Ein grosser Wert bedeutet dagegen, dass die Werte weit verstreut sind. Ein Bild mit vielen Abstufungen wird also eine grosse Standardabweichung aufweisen, ein eintöniges Bild eine kleine Standardabweichung.
grayStdDev = std2(Image);
==== Beispiel 3 ====
Das [[https://github.com/luxeria/foip/blob/master/src/chapter_01/exercise_03.m| Beispiel 3]] zeigt auf wie das Histogramm eines Bildes ermittelt wird als auch die absolute, relative und kummulative Häufigkeit von Pixelwerten.
% load the image
Image = imread(imageSource);
% get histogramm results and do some calculations
[count, value] = imhist(Image);
relCount = count/numel(Image); % calculate relative counts
cumCount = cumsum(relCount); % calculate cumulative count
grayMean = mean2(Image); % calculate mean (2D)
grayStdDev = std2(Image); % calculate standard deviation (2D)
===== Quantisierung =====
Die Quantisierung eines Bildes gibt an, wie fein abgestuft die Pixelwerte sind in Anzahl Bit. Eine Quantisierung mit 8 Bit hat also eine Abstufung der Grauwerte in 256 Schritten, 6 Bit in 64 Schritten, 4 Bit in 16 Schritten usw.
Um ein gegebenes Bild gröber abzustufen z.B von 8 Bit auf 4 Bit, müssen die Pixelwerte dividiert werden durch die Stufendifferenz (8 Bit - 4 Bit = 4 Bit = 16), gerundet auf ganze Zahlen und danach wieder multipliziert werden mit der Stufendifferenz um die richtige Helligkeit zurück zu erhalten.
==== Veranschaulichung vom Informationsverlust ====
Bei der Division und Rundung geht Information verloren, d.h. bei diesem Schritt findet die eigentliche Umwandlung statt. Angenommen wir haben eine Zeile von Pixelwerten zwischen 100 und 120. Es gibt also 20 Abstufungen der Werte.
100 101 102 103 104 105
106 107 108 109 110 111
112 113 114 115 116 117
118 119 120
Dividiert man nun diese durch 16 (4 Bit) und rundet die Ergebnisse so befinden sich die Lösungen in einem Zahlenraum zwischen 25 und 30. Dies sind lediglich 5 Abstufungen statt wie vorher 20. Multipliziert man nun diese Werte wieder mit 16 so ergibt sich
96 96 96 96 96 96
96 96 96 96 96 96
112 112 112 112 112 112
112 112 112
Diese Werte haben nun zwar die Richtigen Helligkeitsstufen damit das Bild einigermassen richtig aussieht, jedoch geht die Detailträue deutlich zurück.
==== Beispiel 4 ====
Das [[https://github.com/luxeria/foip/blob/master/src/chapter_01/exercise_04.m| Beispiel 4]] zeigt wie ein Bild von 8 Bit auf 6, 4, und 2 Bit qantisiert wird.
% load the image
Image = imread(imageSource);
% create the reduced images
Image8bit = Image;
Image6bit = floor(Image8bit/4)*4;
Image4bit = floor(Image8bit/16)*16;
Image2bit = floor(Image8bit/64)*64;
===== Rasterung =====
Die Rasterung beschreibt die räumliche Auflösung (Pixelzahl) eines Bildes (im Gegensatz dazu die Quantisierung, welche die Abstufung der Pixelwerte beschreibt).
Diese kann im Octave mittel der Funktion ''imresize()'' verändert werden.
==== Beispiel 5 ====
Das [[https://github.com/luxeria/foip/blob/master/src/chapter_01/exercise_05.m| Beispiel 5]] zeigt wie ein Bild umgewandelt werden kann auf eine andere Auflösung.
% load the image
Image = imread(imageSource);
% create reduced images
Image1 = Image;
Image2 = imresize(Image1, 0.5);
Image3 = imresize(Image2, 0.5);
===== LUT - look up table =====
Eine LUT (look up table) wird verwendet für die Zuordnung von Pixelwerten. Die Idee und Operation dazu ist relativ simpel. Für jeden Pixelwert gibt es genau einen korresponiderenden Eintag in einer Tabelle (LUT) welcher übernommen wird. Der gelesene Wert im Original dient dabei als Index für die Tabelle (LUT). Es findet also eine lineare und eindeutige Zuordnung von Werten statt beim Anwenden einer LUT.
Im Folgenden ein Prinzipbeispiel einer invertierenden LUT
LUT Index 0 1 2 3 4 5 6 7 8
LUT Wert 8 7 6 5 4 3 2 1 0
----
Image
0 1 1 2 3
1 1 2 3 4
2 3 3 4 5
4 4 5 6 7
5 6 7 8 8
apllyLUT(Image)
8 7 7 6 5
7 7 6 5 4
6 5 5 4 3
4 4 3 2 1
3 2 1 0 0
==== Beispiel 6 ====
Das [[https://github.com/luxeria/foip/blob/master/src/chapter_01/exercise_06.m|Beispiel 6]] zeigt wie verschiedene LUT definiert and angewendet werden.
% load the image
Image = imread(imageSource);
% define look-up-table (LUT)
LUT_inverse = uint8([255:-1:0]);
LUT_bright = uint8([0:255].*2);
LUT_special = uint8(horzcat([0:1:64], [64:2:((255-64)*2+62)]));
% apply LUTs
ImageInverse = intlut(Image, LUT_inverse);
ImageBright = intlut(Image, LUT_bright);
ImageSpecial = intlut(Image, LUT_special);