
Gerade sah der Himmel sehr interessant aus. Des öfteren waren leichte Blitze zu sehen. Also die Spiegelreflex rausgeholt und ein Video vom Himmel gemacht. Mit dem Plan da später dann mal nach Fotos von Blitzen am Himmel zu gucken. Das ganze habe ich dann wenig später auch umgesetzt. Also erstmal das Video in viele Einzelbilder konvertieren. Dabei hilft ffmpeg, die Wunderwaffe für alles, was um Video geht. Einfach mal ffmpeg + (englische Beschreibung, was ihr machen wollt) googlen und sicherlich gibt es bei StackOverflow einen entsprechenden Post dazu. In dem Fall führte dann diese Antwort ans Ziel und hatte auch noch den netten Tipp, das doch besser mit JPGs zu machen. Okay, dann also mal los. Das Video kommt direkt von der SD-Karte:
ffmpeg -i /media/dev/nnnn-nnnn/DCIM/101CANON/MVI_1121.MP4 -vf fps=30 -qscale:v 2 out%d.jpg
Okay, nun haben wir einen Ordner voll mit jpg-Bildern. Die kann man jetzt in einem Bildbetrachter, z.B. eog,
durchschauen und dann den Blitz suchen. Ein guter Viewer schafft es zwar im Daumenkino-Modus, trotzdem muss man noch
einiges an Fotos durchschauen. Bei 30 Frames, die die EOS macht, sind das bei 10 Minuten immerhin 60 * 30 * 10 = 18.000
Fotos. Das muss doch besser gehen. Und eine intuitive Idee war: Gucken wir doch mal, wie hell das Bild insgesamt ist.
Durch den Blitz muss das doch heller werden. Und wieder StackOverflow und schon
kann mittels Variante 4 die Helligkeit bestimmt werden. Dann mal ein Foto anschauen, wie hell das so im Mittel ist ohne
Blitz und das mit ein bisschen Puffer als Grenzwert nehmen. Hier ist es die 16 geworden. Da einfach mal ein bisschen
mit rumprobieren. Hängt stark von den Aufnahmen ab und wie hell insgesamt die Bilder sind.
Zusätzlich noch etwas Logik um alle Dateien in einem Verzeichnis aufzulisten, fertig ist
Variante 1 des Skripts. Die speichert dann auch alle gefundenen Bilder in einer Liste und gibt die am Ende
leerzeichen-getrennt zurück, sodass alle interessanten Bilder einfach mittels cp oder mv in ein extra
Verzeichnis verschoben werden können.
#! /usr/bin/python3
import math
from os import listdir
from os.path import isfile, join
from PIL import Image, ImageStat
def brightness(im_file):
im = Image.open(im_file)
stat = ImageStat.Stat(im)
r,g,b = stat.rms
return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))
if __name__ == '__main__':
mypath = '.'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f)) and f[-3:] == 'jpg']
files = []
for f in sorted(onlyfiles):
bright = brightness(f)
if (bright > 16):
print(f, bright)
files.append(f)
print("Found:")
print(" ".join(files))Lässt man das Skript nun laufen und schaut mal in die Prozessorauslastung (htop), dann fällt auf, dass da irgendwie
nur Prozessorkern was zu tun bekommt. Python läuft halt standardmäßig nur in einem Thread. Aber auch da lässt sich doch
was machen. Also wieder Google angeschmissen und klar, Python hat ne Bibliothek dafür:
multiprocessing
und da gibt es dann auch ein entsprechendes
Tutorial zu.
Letztendlich kurz zusammengefasst: Die Aufgabe muss so runtergebrochen werden, dass sie in mehrere Teilaufgaben
berechnet werden kann. Bei einer Liste an Dateien ist das natürlich relativ einfach, da bekommt jeder Prozessorkern
einfach einen Teil der Liste. Bei n Kernen jeder dann eben aufgerundet 1/n. Damit hat der letzte Kern dann zwar etwas
weniger zu tun, aber die Abweichung ist kleiner als n. Also bei 18.000 Fotos eher zu vernachlässigen. Der Rechner hat
8 Kerne, das macht also dann 18.000/8 = 2.250. Das geht hier sogar genau auf, somit hat jeder Kern 2.250 Bilder, die
er sich anschauen soll.
Der erste Versuch, einfach eine Liste per Referenz an die working-Funktion zu übergeben, ist gescheitert. Die Liste war hinterher einfach leer. Aber auch dafür gibt es natürlich wieder eine Lösung und weniger später gibt es nun den kompletten Code, der alle Kerne auslastet:
#! /usr/bin/python3
import math
import sys
import multiprocessing
import os
from os import listdir
from os.path import isfile, join
from PIL import Image, ImageStat
def brightness(im_file):
im = Image.open(im_file)
stat = ImageStat.Stat(im)
r,g,b = stat.rms
return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))
def working(selected_files, files, start, end, i):
if end > len(files):
end = len(files)
for f in files[start:end]:
bright = brightness(f)
if (bright > 16):
print(f, bright)
selected_files.append(f)
if __name__ == '__main__':
mypath = '.'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f)) and f[-3:] == 'jpg']
thefiles = sorted(onlyfiles)
processes = []
cores = os.cpu_count()
part_work = math.ceil(len(thefiles)/cores)
start = part_work
manager = multiprocessing.Manager()
selected_files = manager.list()
for i in range(0, cores):
end = start + part_work
p = multiprocessing.Process(target=working, args=(selected_files, thefiles, start, end, i))
processes.append(p)
p.start()
start = end
for process in processes:
print(process.join())
print("Done with all threads. Found ")
print(" ".join(selected_files))
print()Und nun final noch das, worauf wir alle warten. Insgesamt ist es doch etwas enttäuschend, aber die Bilder rauschen leider alle sehr und es war auch ziemlich wolkig.

Erstaunlich auch, wie kurz die Blitze tatsächlich sind. Hier drei Frames, die direkt nacheinander kommen. Bei 30 FPS also je etwa 1⁄30 Sekunde = 33 ms auseinander:

Das Skript funktioniert natürlich auch mit Videomaterial, z.B. von YouTube. Schaut da einfach mal nach “Gewitter”, da gibt es auch einiges, z.B. das hier. Testweise habe ich da auch mal das Skript drübergeschickt und es hat die interessanten Bilder aussortiert. So, nun wird’s Zeit die Tabs zu schließen. Der Teil hier ist fertig :)
PS: Ist das schon Machine Learning? :)