Matlab Foto Mosaik

Bei meinem Urlaub in Australien sind so einige Fotos entstanden, welche ich gerne in einem Fotomosaik zusammenstellen wollte. Die im Internet zu findenden Dienste (zum Beispiel makemymosaic: online! Also alle Bilder hochladen …) und Programme (zum Beispiel fmedda) haben mich allerdings nicht sonderlich überzeugt. Viel mehr hatte ich da den Anspruch das selbst zu verwirklichen. Am sinnvollsten erschien mir die Implementierung in Matlab, da diese schnell geht und das fertige Programm mit großen Datenmengen umgehen muss, was eine Stärke von Matlab ist.

Die Implementierung ist wie folgt aufgeteilt, wobei die Punkte 3 bis 5 optional sind und in einem kommenden Beitrag beschrieben werden.

  1. Einlesen aller Bilder
  2. Mosaik berechnen
  3. Vergrößern des Mosaiks (für Poster)
  4. Aufteilen eines großen Fotos
  5. weitere Bilder einlesen und vorhandenes Mosaik verbessern

Eins vorweg: Die Bezeichnung der Variablen ist mir nicht sonderlich gut gelungen. Eigentlich wollte ich die endlich mal systematisch und konsistent benennen aber ganz so schlau wird man aus den Namen doch nicht. Auf meiner todo Liste steht jetzt eine entsprechende Lektüre.

Außerdem erlaubt mir WordPress keine gescheite Formatierung des Codes. Die „echten“ Quelldateien sind selbstverständlich eingerückt!

 

Einschränkungen

Um die Sache etwas einfacher zu machen habe ich mich auf Bilder mit gleicher Auflösung eingeschränkt. Dadurch brauche ich keine Sonderfälle usw berücksichtigen, die die Berechnung verlangsamen und mir die Implementierung verkomplizieren.

 

Einlesen:
01 %% Ziel auswählen
02 [ziel_fn, ziel_pn] = uigetfile('*.*');
03 ziel = imread(strcat(ziel_pn, ziel_fn));
04 new_img = ziel;
05
06 pix_s1 = size(ziel, 1);
07 pix_s2 = size(ziel, 2);
08
09 teiler = 138;
10
11 pix_teil_s1 = pix_s1 / teiler;
12 pix_teil_s2 = pix_s2 / teiler;
13
14
15 %% Komponenten auswählen
16 path = uigetdir(pwd, 'Ordner auswählen -> .jpg');
17 files = dir(fullfile(path, '*.jpg'));
18
19 files_count = size(files, 1);
20
21 pictures = zeros(files_count, pix_teil_s1, pix_teil_s2, 3, 'uint8');
22 for idx = 1:files_count
23 disp(idx);
24 pictures(idx,:,:,:) = imresize(imread(strcat(path,'\',files(idx).name)), 1/teiler);
25 end

Per Dialog wird das Zielbild in ziel eingelesen. teiler in Zeile 09 definiert, dass jede Zeile und Spalte in 138 gleichgroße Teile aufgeteilt sein soll. Bei einer Bildgröße von 4416×3312 Pixeln ergibt das 32×24 Pixel je Teilbild bei insgesamt 19.044 Mosaikelementen. Im nächsten Dialog wählt man die zu ladenden Bilder aus, die daraufhin verkleinert eingelesen werden. Um den Faktor 138 verkleinert werden sie in pictures gespeichert.

 

Mosaik berechnen:

Kurzfassung: Jedes einzelne Element wird betrachtet und mit allen Bildern verglichen. Das am besten passende wird ausgewählt – es kann sich dabei auch um ein horizontal gespiegeltes Bild handeln.

01 %% Ziel auswählen
02 [ziel_fn, ziel_pn] = uigetfile('*.*');
03 ziel = imread(strcat(ziel_pn, ziel_fn));
04 new_img = ziel;
05
06 pix_s1 = size(ziel, 1);
07 pix_s2 = size(ziel, 2);
08
09 %teiler = 17*2*2;
10
11 pix_teil_s1 = pix_s1 / teiler;
12 pix_teil_s2 = pix_s2 / teiler;
13
14 %% Bilder auswählen
15 new_img_idx = zeros(teiler);
16 new_img_gleich = zeros(teiler);
17
18 tic
19 for d1=1:teiler
20 disp(d1);
21 idx_1 = 1+(d1-1)*pix_teil_s1 : d1 * pix_teil_s1;
22 for d2=1:teiler
23 idx_2 = 1+(d2-1)*pix_teil_s2 : d2 * pix_teil_s2;
24 subziel = ziel(idx_1, idx_2, :);
25 subziel2 = repmat(reshape(subziel,1,pix_teil_s1,pix_teil_s2,3), [files_count 1 1 1]);
26 gleich = sum(sum(sum(imabsdiff(subziel2,pictures),2),3),4);
27 gleich_f = sum(sum(sum(imabsdiff(subziel2,flipdim(pictures,3)),2),3),4);
28
29 [w,i] = min(gleich);
30 [w_f,i_f] = min(gleich_f);
31 if w <= w_f
32 new_img_idx(d1,d2) = i;
33 new_img_gleich(d1,d2) = w;
34 new_img(idx_1, idx_2, :) = pictures(i,:,:,:);
35 else
36 new_img_idx(d1,d2) = -i_f;
37 new_img_gleich(d1,d2) = w_f;
38 new_img(idx_1, idx_2, :) = flipdim(pictures(i_f,:,:,:),3);
39 end
40 end
41 end
42 toc
43
44 imshow(new_img);

Die Zeilen 01 bis 12 wiederholen sich hier, damit dieses Skript unabhängig vom ersten ausgeführt werden kann und man so verschiedene Zielbilder berechnen kann. Bevor die Berechnung in Zeile 19 beginnt, wird zunächst mit new_img_idx ein 2D-Array definiert, welches die Indexwerte der einzusetzenden Bilder abspeichert. Das 2D-Array new_img_gleich speichert die „Gleichheit“ der eingesetzten Bilder – dazu später mehr. Beide Arrays haben hier die Größe 138×138.

Folgende Programmschritte laufen jetzt ab:

  1. Zeilen 19 bis 21: Die hier definierte Schleife fährt in der ersten Dimension („runter“) jedes Element ab und gibt die aktuelle Position in der Kommandozeile an. In idx_1 werden die zu verarbeitenden Pixel des Zielbildes ausgewählt.
  2. Zeilen 22 und 23: Die hier definierte zweite Schleife fährt die zweite Dimension („rechts) ab. Durch beide Schleifen wird jedes Element nacheinander abgerastert. Prinzipiell ließen sich beide Schleifen auch durch Matrizen-Befehle für die folgenden Zeilen ersetzen. Aus Gründen der Komplexität wurde dies nicht umgesetzt. idx_2 ist analog zu idx_1 definiert.
  3. Zeile 24: Zuerst wird aus dem Zielbild das subziel ausgeschnitten, also derjenige Bereich, der im aktuellen Schleifendurchlauf berechnet werden soll – ein Mosaikstück also. Der dritte Index „:“ wählt dabei in der dritten Dimension alle Elemente aus, was bei einem Bild dem Rot-, Grün-, und Blaukanal enspricht.
  4. Zeile 25: Um Matlabs Stärke der Matrizenberechnung auszunutzen (siehe Zeilen 26 bis 30), wird subziel nun im 4D-Array subziel2 entsprechend der Anzahl der Vergleichsbilder wiederholt. Konkret bedeutet das, dass subziel2 zum Beispiel bei 1000 Vergleichsbildern 1000 mal den grade betrachteten Bildausschnitt abgespeichert hat.
  5. Zeile 26: Hier findet der eigentliche Vergleich statt: subziel2 und das gleichdimensionale Array pictures werden voneinander subtrahiert. Ergebnis ist für jedes Rot, Grün und Blau Pixel deren absolute Differenz. Danach werden je Bild (Dimension 1) alle Spalten (Dimension 2, sum(..., 2)), alle Zeilen (Dimension 3, sum(...,3)) aufaddiert. Ergebnis dieser Operation ist für jedes Bild je Farbkanal die absolute Abweichung der Pixelfarbwerte. Zum Schluss wird die Summe über die Farbkanäle gebildet (Dimension 4, sum(...,4)).
  6. Zeile 27: Analog zu Zeile 26 wird hier für die horizontal gespiegelten Bilder die Differenz gebildet.
  7. Zeilen 29 und 30: Es wird nun das Bild mit der geringsten Differenz jeweils für „normal“ und gespiegelt“ bestimmt.
  8. Zeilen 31 bis 39: Nun wird ausgewählt, welches der beiden Bilder (normal oder gespiegelt) das bessere ist, ausgewählt und in das Gesamtbild new_img eingesetzt.
  9. Zeile 44: Darstellen des Ergebnisses!

 

 

Ergebnisse:

Das Originalbild:

IMG_1776a

Das Ergebnis mit den oben gezeigten Skripten bei einer Auswahl von 696 Bildern:

test83

Trotz der kleinen Auswahl an Bildern kann sich das Ergebnis schon sehen lassen.

Zum Vergleich mal das Ergebnis mit fmedda in der freien Version (Datenbank mit 100×100 Pixeln mit den selben 696 Bildern), bei ~19700 Elementen (138×138 = 19044 ging nicht genau, warum auch immer). Alle sonstigen Einstellungen blieben in der Standardeinstellung. Leider wurden die Ränder abgeschnitten, dafür ging die Berechnung schneller:

test83fme696

Ehrlich gesagt gefällt mir mein Ergebnis mehr 😉

Die Quelldateien werden nachgereicht.