Ein Kommandozeilen-Tool für den digitalen Fotoprozess Wie man auch bei mehreren Kameras und vielen Fotoprojekten den Überblick behält.

In dem Fotoworkflow-Blog wird der gesamte Ablauf für die Fotoentwicklung vorgestellt, so dass der Weg von der Kamera-Speicherkarte bis zur Veröffentlichung bzw. Archivierung beschrieben ist. Der Nachteil des so durchgeführten Workflows liegt auf der Hand: da ist einen Menge Handarbeit auf der Konsole bzw. mit dem Datei-Browser zu machen. Das ist zeitraubend, fehleranfällig und einfach nervig. Die Lösung für mich sind eine handvoll Kommandozeilenbefehle die genau die einzelnen Schritte als Befehl umsetzen. Dieses Tool, fow genannt, wird hier nun genauer vorgestellt.

Technische Umsetzung

Als Sprache wurde Python3 gewählt, da die Befehle damit schnell implementiert werden konnten. Die Befehle benötigen, neben Python 3, folgende Tools, welche aber in der Regel bereits installiert sind:

  • exiv2
  • exiftool
  • rsync

Das Projekt ist unter gitHub gehostet und steht als Open Source zur Verfügung. Vorschläge für Verbesserungen und Fehlermeldung sind willkommen und können dort erfasst werden. Wer aktiv mitentwickeln möchte, kann dies gerne tun.

Installation

Voraussetzung: Linux-System, auf dem DEB-Pakete installierte werden können (Debian, Ubuntu, Lint etc.). Für Windows-PCs lässt sich fow in einer VirtualBox z. B. mit Lint/Mate betreiben. Download: Neuestes Deb-Paket von gitHub-Projekt fow Unterverzeichnis /dist herunterladen und in einer Shell installieren mit z . B.:

$ sudo dpkg -i fow_1.1.8_all.deb

Initialisierung

Man benötigt ein, am besten leeres, Unterverzeichnis und initialisiert das fow, indem man in diesem Verzeichnis ‚init‘ aufruft. Beispiel:

$ mkdir ~/photo
$ cd ~/photo
$ fow init

Damit wird die Verzeichnisstruktur aufgebaut und ein paar Konfigurationsvariblen initialisiert. Zur Erfolgskontrolle lässt man sich die Konfigurationswerte anzeigen:

$ fow config
backup.path = None
gps.tracks = None
task = None

Ok, dass sieht noch nicht wirklich eingerichtet aus. Dass erfolgt erst bei Bedarf der einzelnen Befehle, für den Anfang genügt es. Hier ein paar grundlegende Tipps für den Anfang:

  • Das Wurzelverzeichnis hat keinen Einfluss auf fow und kann beliebig umbenannt und verschoben werden
  • Man muss sich innerhalb der Verzeichnisstruktur befinden, um einen fow-Befehl ausführen zu können
  • Fow-eigene Unterverzeichnisse dürfen nicht gelöscht/umbenannt werden
  • Zusätzliche Unterverzeichnisse sind möglich
  • Man kann beliebig viele fows nebeneinander betreiben
  • fows können nicht geschachtelt werden

Prinzip

Die Befehle sind so angelegt, dass sie mit möglichst wenig Tipparbeit einen Workflow-Schritt oder Befehl ausführen. Jedem Befehl geht das ‘fow’ voraus. Ein Übersicht über die Befehle bekommt man mit help:

$ fow help
fow commands:
backup - save the fow to an external directory
config - show and change settings of the fow. A setting is a key value pair.
exif - Set or show EXIF values for final images of the actual task
export - Copy final images to external destinations for the actual task
gps - add gps locations from gpx files
help - This help. Use "help <command>" for a specific command help
init - Creates new fow in the actual directory
load - Loads images from external destinations into 00_Inbox.
rename - Move and rename files from 00_Inbox to 01_Import.
show - reporting for processing steps
task - manage a fow's task

Detaillierte Hilfe zu den einzelnen Befehlen bekommt man mit help , z. B.:

$ fow help init

Ein Befehl kann Optionen (beginnend mit 2 Bindestrichen) und Parameter haben. So habe z. B. viele Befehle eine verbose-Ausgabe (–verbose). Ändernden Befehle bieten in der Regel eine Testoption (–test), die keine Änderungen macht (Dry-run). Beispiele:

$ fow rename --test
Processing jpg 5: 5
Processing raw 3: 3
Processing video 0: 0
8 files will be moved and renamed.
$ fow rename --verbose
Processing jpg 5: 5
Processing raw 3: 3
Processing video 0: 0
  OK  jpg 20170720-181117-DSCF5339.JPG --> 20170720-181117-20170720-181117-DSCF5339.JPG
  OK  jpg 20170720-184415-DSCF5343.JPG --> 20170720-184415-20170720-184415-DSCF5343.JPG
  OK  jpg 20170720-184415-DSCF5344.JPG --> 20170720-184415-20170720-184415-DSCF5344.JPG
  OK  jpg 20170720-184416-DSCF5345.JPG --> 20170720-184416-20170720-184416-DSCF5345.JPG
  OK  jpg 20170720-184650-DSCF5346.JPG --> 20170720-184650-20170720-184650-DSCF5346.JPG
  OK  raw 20170720-184415-DSCF5343.RAF --> 20170720-184415-20170720-184415-DSCF5343.RAF
  OK  raw 20170720-184415-DSCF5344.RAF --> 20170720-184415-20170720-184415-DSCF5344.RAF
  OK  raw 20170720-184416-DSCF5345.RAF --> 20170720-184416-20170720-184416-DSCF5345.RAF
8 file(s) moved.

Tipp: Eine Option hat auch eine Abkürzung, bestehend aus einem Bindestrich und dem Anfangsbuchstaben der Option, zum Beispiel kann statt –verbose auch einfach -v geschrieben werden. Zur besseren Lesbarkeit des Blogs wird hier aber nur die lange Schreibweise verwendet.

Die einzelnen Workflowschritte

Das Workflow Diagramm, wie es schon im Fotoworkflow-Blog vorgestellt wurde, dient fow als Grundlage und wurde nur leicht angepasst.

Workflow Workflow

Folgende Schritte werden von fow nicht unterstützt, da es für sie vorhandene Tools gibt:

  • Develop: Nachbearbeitung der JPG- bzw. RAW-Bilder erfolgt mit dem jeweiligen Lieblingstool, z. B. Darktable. Ferner fällt hierunter auch das Hinzufügen von Titel, Beschreibung etc.
  • Clean: Nicht mehr benötigte fow-Tasks sind manuell zu löschen

LOAD

Aufgabe
Laden aller Bilddateien (incl. aus Unterverzeichnissen) aus einer externen Quelle (wie z. B. SD-Karte oder Smartphone) in den Eingangsordner 00_Inbox.

Endzustand
Alle Bilddateien der externen Quelle wurden nach 00_Inbox kopiert bzw. verschoben und liegen nun in einem der Unterordner.

Load-Verzeichnis Load-Verzeichnis

Voraussetzung
Die SD-Karte ist gemountet.

Durchführung
Das Mount-Verzeichnis der SD-Karte wird einmal als config-Variable gesetzt:

$ fow config -s=load.sd=/mount/sd-card

Nun kann man sämtliche Bilddateien importieren lassen:

$ fow load sd
Processing jpg 3: 3
Processing raw 1: 1
Processing video 0: 0
All 3 files copied (0 existing files ignored).

Hinweise
Mit der Option –move werden die Dateien nach dem Kopieren aus der Quelle gelöscht. Das ist mit Vorsicht zu verwenden, schließlich hat man noch kein Backup der Bilddateien gemacht. Mit der Option –force werden vorhandene Dateien überschrieben

BACKUP
Es wird einmalig ein Backup-Verzeichnis ausserhalb des fow-Vezeichnisses definiert. Mit Backup kann dann man zu einem beliebigen Zeitpunkt die Daten dorthin sichern. Ein guter Zeitpunkt ist direkt nach dem Load, damit hat man einen definierten Zustand des fow-Verzeichnisses und kann mit ruhigem Gewissen die Daten von der SD-Karte löschen.

Aufgabe
Sichern des gesamten fow-Verzeichnisses auf ein externes Verzeichnis, idealerweise eine externe Festplatte oder der lokale Server.

Endzustand
Eine Kopie das fow-Verzeichnisses liegt vor.

Voraussetzung
Das externe Medium muss gemountet sein.

Durchführung Es wird einmalig das Backup-Verzeichnis als Config-Wert definiert.

$ fow config -s=backup.path=/media/diskstation/fow-backup

Nun kann das Backup durchgeführt werden.

$ fow backup  
Starting backup to "/media/diskstation/fow-backup".  
sending incremental file list  
.fow/  
.fow/setting.pickle  
00_Inbox/  
00_Inbox/jpg/  
00_Inbox/jpg/20170202-103548-DSCF4580.jpg  
00_Inbox/jpg/20170720-181117-20170720-181117-DSCF5339.JPG  
00_Inbox/jpg/20170720-184415-20170720-184415-DSCF5343.JPG  
00_Inbox/jpg/20170720-184650-20170720-184650-DSCF5346.JPG  
00_Inbox/jpg/DSCF4580.jpg  
00_Inbox/jpg/DSCF4593.jpg  
00_Inbox/raw/  
00_Inbox/raw/20170202-103548-DSCF4580.raf  
00_Inbox/video/  
01_Import/  
01_Import/jpg/  
01_Import/raw/  
01_Import/raw/20170720-184416-20170720-184416-DSCF5345.RAF  
01_Import/video/  
02_Progress/  
02_Progress/blog/  
02_Progress/blog/labels/  
02_Progress/blog/labels/final/  
02_Progress/blog/labels/final/20170226-103222-DSCF4702.jpg  
...  
Number of files: 95 (reg: 45, dir: 50)  
Number of created files: 95 (reg: 45, dir: 50)  
Number of deleted files: 0  
Number of regular files transferred: 45  
...  
sent 124,487,247 bytes  received 1,106 bytes  1,791,199.32 bytes/sec  
total size is 124,452,210  speedup is 1.00  

Hinweis
Es wird nur ein Backup unterstützt, keine Rotierung

Intern wird rsync für den Befehl verwendet.

RENAME

Auf den verschiedenen Kameras habe die Bilddateien unterschiedliche Namensregeln. Mit rename werden die Bilddateien einheitlich umbenannt und bekommen einen Zeitstempel. Das vereinfacht das Dateimanagement erheblich. Die Umbenennung erfolgt auf Basis der Exif-Informationen. Eine Bilddatei wird nach folgendem Muster benannt:
<yyyymmdd>-<hhMMss>-<Dateiname mit Dateisuffix>
Die Dateien bekommen also einen Zeitstempel vorangestellt und sind damit chronologisch sortierbar. Der ursprüngliche Dateiname wird angehängt. Das hat den Vorteil, das es keine Namenskonflikte bei zeitgleichen Aufnahmen gibt. Außerdem wird das Duplizieren verhindert, falls ein Dateien erneut geladen und umbenannt werden.

Vor- und nach der Umbenennung Vor- und nach der Umbenennung

Aufgabe
Umbenennung aller Bilddateien aus 00_Inbox und verschieben nach 01_Import.

Endzustand Keine Bilddateien mehr in 00_Inbox und in 01_Import liegen die verschobenen Bilddateien vor.

Durchführung Die Durchführung ist denkbar einfach und kann je nach Menge ein paar Sekunden dauern.

$ fow rename
Processing jpg 5: 5
Processing raw 2: 2
Processing video 0: 0
7 file(s) moved.

Hinweis
Fehlt das Exif-Datum, wird nur verschoben.

ORGANIZE

Verteilen der importierten Bilddateien auf einzelne, unabhängige Aufgaben (Task).

Ausgangssituation
Eine Menge von Bildern, evtl. für unterschiedliche Zwecke, liegen in 01_Import vor.

Endzustand
Einige oder alle Bilddateien wurden in unterschiedliche ‘Tasks’ verschoben. Nicht mehr benötigte Bilder wurden gelöscht.

Durchführung
Hier ist Handarbeit mit dem Bildbetrachter bzw. Dateibrowser gefragt. fow unterstützt bei der Verwaltung der Task-Verzeichnisse und kann RAW-Dateien dorthin nachziehen.
Das Verzeichnis 01_Import/jpg wird mit einem beliebigen Bildbetrachter gesichtet. Zusammengehörige Bilder werden in einen neuen bzw. bestehenden Task verschoben. Evtl. vorhandene Raw-Dateien nachgezogen. Nicht mehr benötigte Bilder werden aus 01_Import gelöscht.

Ein Task ist eine Verzeichnisstruktur unter 02_Prozess. Tasks liegen aber nicht direkt unter 02_Prozess, sonder werden weiter in Unterverzeichnisse gruppiert.
Hat man z. B. Fotos für ‘Bild des Tages’ vom 1.9. und 2.9.2017, legt man sich zuerst die Tasks für beide Tage an. Beispiel:

$ fow task -c=daily/20170901 
$ fow task -c=daily/20170902

Damit werden die Task-Verzeichnisse angelegt:

Task-Verzeichnisstruktur Task-Verzeichnisstruktur

Nun kann man die JPG-Bilder in die Tasks verschieben.

Bei fow ist immer nur ein Task aktiv. Im aktive Task kann man u. a. die RAW-Dateien aus 01_Import nachziehen. Einen vorhanden Task kann man wie folgt aktivieren:

$ fow task -a=daily/20170901

Man kann auch durch Vorwärts- bzw. Rückwärtsblättern (Option –next bzw. –previous) den aktiven Task wechseln:

$ fow task --next   
   daily/20170901  
*  daily/20170902  
   dienstreise/hamburg17  

Hat man neben JPGs noch RAW-Dateien erstellt, kann man diese RAWs in den aktuelle Task aus 01_Import/raw nachziehen. Mit der Test-Option kann man auch erst mal sehen, ob noch RAWs in 01_Import existieren:

$ fow task --raw-import --test  
raw files to move (may already exists):  
20170202-103548-DSCF4580.raf  

DEVELOP

Unter Develop wird der gesamte Prozess der Fotobearbeitung innerhalb eines Tasks zusammengefasst, also die Erstellung des Endbildes auf Basis eines RAW bzw. JPGs mit Hilfe von Bildbearbeitungsprogramme. Dabei wird davon ausgegangen, dass das Originalbild nicht verändert wird, sondern dass die Programme mit Sidecar-Dateien (XML) oder Projektdateien (z. B. XCF bei Gimp) arbeiten und das Ergebnis in eine neue Datei exportiert werden (z. B. jpg).

Verzeichnis von Task ‘weekly/20170205’ Verzeichnis von Task ‘weekly/20170205’

Fow unterstützt hier durch Vorgabe der Verzeichnisstruktur des Tasks: In jpg und raw liegen die Originaldateien, in work können ggf. Projektdateien abgelegt werden und final dient als Exportverzeichnis für das fertige Produkt.

Ausgangssituation
Originalbilder zur Bearbeitung sind im Task abgelegt (jpg, raw).

Endzustand
Endbilder liegen im Ordner ‘final’.

Durchführung
Erfolgt mit den jeweiligen Bildbearbeitungsprogrammen. Der letzte Schritt ist der Export nach final. Z. B. Darktable unterstützt das Festlegen des Exportverzeichnisses.

Export bei Darktable Export bei Darktable

Mit fow kann man sich einen Überblick über den Bearbeitungszustand eines Task verschaffen, z. B.:

$ fow task --long   
Actual task is weekly/20170205. Listing all image files.  
Reading /photo-workflow/02_Progress/weekly/20170205/jpg 2: 2  
Reading /photo-workflow/02_Progress/weekly/20170205/raw 1: 1  
Reading /photo-workflow/02_Progress/weekly/20170205/video 0: 0  
Reading /photo-workflow/02_Progress/weekly/20170205/final 1: 1  
jrftg 20170202-103548-DSCF4580 Ramification  
j--tg 20170205-180804-DSCF4593 Ronneburg  

Das Beispiel zeigt, dass es zwei Bilder gibt. Das erste trägt den Titel (t) ‘Ramification’, es hat Originalbilder in jgp (j) und raw (r), wurde bereits nach final (f) exportiert und hat gps-Daten (g). Das zweite Bild hat auch bereits einen Titel, ‘Ronneburg’, hat gps-Daten und liegt nur als Original-jpg vor.

Der Befehl ’task’ bietet noch weitere Optionen, u. a.:

  • –activate : Aktivieren eines bestimmten Tasks
  • –next und –previous: Wechseln in den nächsten bzw. vorherigen Task
  • –fill-final: Ergänzen des final-Verzeichnisses aus jpg

EXIF-Tags

Zum Setzen der EXIF-Tags für Titel, Beschreibung und Author, gibt es den Befehl ’exif’. Hier ein Beispiel wie alle drei Tags auf einmal gesetzt werden können:

$ fow exif -f -t  'Pendel LXIIX' -d 'Und Autos rauschen vorbei' -a 
20181205-192255-DSCF7592.jpg   
  o title             T 2 -> Pendel LXIIX  
  + description       Und Autos rauschen vorbei  
  + author            Christian Schulzendorff 2019, CC BY-SA 2.0  

Den Wert für Author muss man sich nur einmalig erstellen und wird dann nicht mehr explizit gesetzt:

$ fow config -s 'exif.author=Christian Schulzendorff {YYYY}, CC BY-SA 2.0'

Ebenso kann man sich gesetzten Exif-Werte mit ‘fow exif’ anzeigen lassen.

LOCATION

Nicht alle Kameras taggen GPS-Informationen an die Bilder. Oft sollen aber trotzdem die Ortsdaten (Längen- und Breitengrad, ggf. Höhe) dem Bild hinzugefügt werden. Dazu muss dann für jedes Bild die aufgezeichnete Trackdatei ausgewertet werden. Manche Navi-Herstellen bieten dazu Programme an, oder man bedient sich Programmen wie z. B. geotag. Alternativ bietet fow die Möglichkeit, die finals eines Tasks mit Tracks in einem fest definierten Verzeichnis zu taggen.

Ausgangssituation Finals (Dateien im Task-Verzeichnis ‘final’) sind erstellt und das Verzeichnis mit Trackdateien (gpx) ist gemountet.

Endzustand
Final-Dateien sind getaggt.

Durchführung
Einmalig muss das externe Verzeichnis mit den Trackdateien als config-Wert festgelegt werden.

$ fow config -s=gps.tracks=/media/tracks

Die gpx-Dateien müssen den Datumsstempel im Format yyyymmdd im Namen haben, damit sie erkannt werden, z. B.:

$ find /media/tracks/ -name '*2017*02*0[2|5]*'  
../tracks/20170205.gpx  
../tracks/Christian_Schulzendorff_2017-02-02_09-10-10.gpx  

Das taggen geht dann für den aktiven Task sehr einfach:

$ fow gps -verbose  
Reading /photo-workflow/02_Progress/weekly/20170205/final 2: 2  
Path to images: /photo-workflow/02_Progress/weekly/20170205/final  
Path to tracks: /media/tracks  
+g 20170202-103548-DSCF4580.jpg Christian_Schulzendorff_2017-02-02_09-10-10.gpx  
+g 20170205-180804-DSCF4593.jpg 20170205.gpx  
2 images processed, 2 gps data set (where 0 existing gps data were changed)  

EXPORT

Sind alle Bilder eines Tasks in final erstellt, werden diese von dort an ihren Bestimmungsort verteilt. Fow unterstützt dabei den lokalen Export in beliebig viele Zielordner. Für andere Ziele, wie z. B. Flickr, Instagram u. s. w. bietet fow keine Unterstützung. Beim Export werden die Bilddateien immer nur kopiert, niemals verschoben.

Ausgangssituation
Alle Bilder liegen im Task-Ordner ‘final’ bereit und die Exportziele sind definiert.

Endzustand
Final-Dateien wurden an alle Bestimmungsorte exportiert.

Durchführung
Einmalig müssen die Exportziele definiert werden, z. B. für das Jahresarchiv.

$ fow config -s=export.archiv=../archiv/2017

Nun können die logischen Pfade für den Export verwendet werden:

$ fow export archiv  
2 images in weekly/20170205/final  
Destination: ../archiv/2017  
Copying 20170202-103548-DSCF4580.jpg. Done.  
Copying 20170205-180804-DSCF4593.jpg. Done.  

Existiert die Datei bereits in dem Exportverzeichnis, kann das Überschreiben mit –force erzwungen werden. Das ist nützlich, wenn finals nach dem Export doch noch mal geändert wurden.

Alternativ kann auch durch Pfadangabe direkt in ein Exportziel kopiert werden.

$ fow export --path=~/Bilder/Diashow/

CLEAN UP

Ein Task ist etwas Temporäres und hat mit der Erzeugung der final-Bilder und dem anschließenden Export in der Regel ausgedient. Fow bietet zum Aufräumen keine Unterstützung.
Wird ein Task nicht mehr benötigt und wurden alle relevanten Dateien aus dem Task kopiert, ist das Taskverzeichnis z. B. per rm zu löschen:

$ rm -r 02_Progress/weekly/20170226

Hinweis
Der aktive Task sollte nicht gelöscht werden, da noch der config-Eintrag dazu existiert).