Autor: Mathias Lipowski

Cinematic-Look für Videoaufnahmen

Framerate

Die für das menschliche Auge angenehmste Framerate liegt bei 24fps. Daher werden Kinofilme auch meist (mit wenigen Ausnahmen) in dieser Rate gezeigt. Man sollte ein Video also gleich in dieser Framerate aufnehmen. Bei Zeitraffer- oder Slowmotion-Aufnahmen (also anderen Frameraten) ist es ratsam, diese dann mit 24fps abzuspielen.

Farbprofil

Ein C-LOG (Custom LOG) Farbprofil verwenden. C-LOG erweitert den Dynamikbereich von Schatten. Dies bedeutet, dass die Glanzlichter besser belichtet werden und der Dynamikbereich eines Videos maximiert werden kann.

Nicht alle Canon-Kameras können Videos im C-LOG Farbprofil erstellen, man kann allerdings ein Farbprofil so einstellen, um dem möglichst Nahe zu kommen:

Kamerawinkel

Neutral, also in direkter Augenhöhe des Betrachters, wirkt auch neutral. In diesem Winkel ist der Horizont etwa in der Mitte der Aufnahme.

Ein Kamerawinkel von oben auf eine Person eher erniedrigend bzw. der dargestellten Person überlegen. In diesem Winkel ist fast nur Boden zu sehen. Beachten sollte man dabei, dass der Hintergrund nicht zu unaufgeräumt bzw. dreckig aussieht, um der Aufnahme keine ungewollten Störungen einzubringen.

Im Gegensatz dazu hebt ein Kamerawinkel von unten eine Person bzw. auf ein Objekt hervor. Dieser lässt eine Person überlegen erscheinen, fast majestätisch. Hierbei ist fast nur Himmel (oder ähnliches) im Hintergrund zu sehen, was die Aufnahme auch mehr aufgeräumt und „ruhig“ wirken lässt.

Was fehlt?

Du hast weitere Tipps für einen Cinematic-Look von Videoaufnahmen? Schreibe es einfach in die Kommentare.

Bildquelle unsplash.com

Notiz an mich: jetzt noch schneller mit Postausgang

Die schnellste Notiz-App wird nun noch schneller: erstellte Notizen werden nun im Postausgang gespeichert und im Hintergrund gesendet. Damit entfällt das lästige Warten, bis die Notiz gesendet wurde. Und: Du behältst den Überblick, welche Notizen zuletzt erstellt wurden.

Was ist noch neu?

  • Viele Notiz- oder Aufgaben-Apps (Evernote, Trell, Any.do, Wunderlist, …) unterstützen den Import von Aufgaben über Email, d.h. eine Email an diese Dienste erstellt automatisch eine neue Aufgabe. Notiz an mich hilft dabei, das richtige Format zu erstellen. So wird bspw. häufig der Betreff als Aufgabe und der Inhalte der Email als Beschreibung verwendet.
  • Wir haben das Today-Widget aktualisiert: in den Einstellungen lässt sich nun festlegen, welche Aktionen (in welcher Reihenfolge) auf dem Widget dargestellt werden sollen.

Du kannst das Update der iOS-App im AppStore laden.

Less (CSS) unter macOS installieren

Der einfachste Weg Less auf einem Server zu installieren ist über npm (dem node.js Paketmanager) mit:

$ npm install -g less

Wenn nicht vorhanden: Command Line Tools für Xcode installieren

xcode-select --install

Wenn nicht vorhanden: HomeBrew installieren

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Wenn nicht vorhanden: Node.js installieren

brew install node

LessCSS installieren

sudo npm install -g less

Nun lassen sich die lessc Befehle ausführen.

Fatal error: Call to undefined function bindtextdomain()

Fatal error: Call to undefined function bindtextdomain() in [...]

Tritt der Fehler auf, dann fehlt möglicherweise die PHP-Erweiterung Gettext. Diese kann in der php.ini aktiviert werden:

extension=php_gettext.dll

Ist diese Erweiterung noch nicht vorhanden, dann lässt sich diese unter macOS (am Beispiel von PHP7.0) über MacPorts installieren:

sudo port install php70-gettext

Apache neustarten:

sudo port unload apache2
sudo port load apache2

OpenCV Python: eigene Haar Cascade erstellen

Um eine Haar Cascade zu erstellen werden „positive“ und „negative“ Bilder benötigt. „Positive“ Bilder enthalten das Objekt welches gefunden werden soll. Das können entweder Bilder sein, welche ausschließlich das Objekt enthalten oder Bilder, welche (neben anderen Inhalten) das Objekt enthalten, wobei hier die ROI (region of interest) angegeben werden muss. Mit diesen positiven Bildern wird eine Vektordatei erstellt, was im Grunde nichts anderes ist, als alle positiven Bilder zusammen.

Theoretisch ist ein positives Bild und einige tausend negative Bilder ausreichend. Aus dem positiven Bild lassen sich durch verschiedenes „Rauschen“ die notwendige Anzahl erstellen. Die negativen Bilder können alles mögliche enthalten (außer das Object selbst).

Als grober Richtwert: das Verhältnis von positiven zu negativen Bildern sollte etwa 2:1 sein.

Pfade für Grafiken:

workspace 
-- pos
-- neg
-- data

Die Grafiken sollten vor dem Training entsprechend verkleinert werden. Positive Grafiken sollten möglichst klein gehalten werden. 50 x 50px sollten für den Anfang ausreichen, diese bringen bereits gute Ergebnisse. Bei den negativen Grafiken verwenden wir 100 x 100px. Je größer die Grafiken, desto länger dauert das Training.

Hier ein einfaches Skript zum Verkleinern der Grafiken resize-images.py:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import cv2
import numpy as np
import glob
import os
import getopt
import sys
from os.path import basename

imgPath = "./"
imgPathOut = ""
resizeDim = 400

usage = '''usage: resize-image.py -i  -o  -d '
    
    -i   : input directory
    -o   : output directory (optional, same as input if empty)
    -d   : dimension of output image (px)
    '''

try:
    opts, args = getopt.getopt(sys.argv[1:],"hi:o:d:",["ipath=","opath=","d="])
except getopt.GetoptError:
    print usage
    sys.exit(2)
for opt, arg in opts:
    if opt == '-h':
        print usage
        sys.exit()
    elif opt in ("-i", "--ipath"):
        imgPath = arg
    elif opt in ("-o", "--opath"):
        imgPathOut = arg
    elif opt in ("-d", "--d"):
        resizeDim = int(arg)

# Check if output path is empty
if not imgPathOut:
    imgPathOut = imgPath

# Check for images
if len(list(glob.iglob('%s/*.jpg' % imgPath))) == 0:
    print 'Could not find any images in path "%s".' % (imgPath)
    sys.exit(2)

# Create output directory if it does not exist
if not os.path.exists(imgPathOut):
    os.makedirs(imgPathOut)

# Loop trough images
for filename in glob.iglob('%s/*.jpg' % imgPath):
    imgName = basename(filename)
    print '%s/%s' % (imgPath, imgName)

    # Read image
    img = cv2.imread(filename)
    # Resize
    img = cv2.resize(img, (resizeDim, resizeDim), interpolation = cv2.INTER_AREA)
    # Convert to grayscale
    img = cv2.cvtColor(img, cv2.COLOR_RGBA2GRAY)
    # Write image
    cv2.imwrite('%s/%s' % (imgPathOut, imgName), img)

Besitzen die Grafiken nun eine geeignete Größe, können wir zum Erstellen der Trainingslisten übergehen. Hier ein Skript zum Erstellen der Trainingsdateien (Datenlisten) create-pos-n-neg.py:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import numpy as np
import cv2

def create_pos_n_neg():

    fpos = open('pos.lst','w')
    fneg = open('neg.lst','w')

    for file_type in ['neg', 'pos']:
        
        for img in os.listdir(file_type):

            if file_type == 'pos':
                try:
                    image = cv2.imread(file_type+'/'+img)
                    h, w, channels = image.shape
                    line = file_type+'/'+img+' 1 0 0 '+str(w)+' '+str(h)+'\n'
                    fpos.write(line)
                except:
                    continue
            elif file_type == 'neg':
                line = file_type+'/'+img+'\n'
                fneg.write(line)

    fpos.close()
    fneg.close()

create_pos_n_neg()

Die Dateien pos.lst und neg.lst enthalten nun die Informationen zu den positiven und negativen Grafiken.

Aus den positiven Grafiken muss nun die Vektordatei erstellt werden, in der alle positiven Grafiken zusammengefasst werden. Dazu wird opencv_createsamples verwendet!

opencv_createsamples -info pos.lst -num 2500 -w 20 -h 20 -vec pos.vec

Nun kann das Training beginnen:

opencv_traincascade -data data -vec pos.vec -bg neg.lst -numPos 2000 -numNeg 1000 -numStages 10 -w 20 -h 20

Dabei wird angegeben, wo die Daten gespeichert werden, wo Vektordatei und Hintergrunddatei ist, wie viele positive und negative Grafiken verwendet werden sollen, die Anzahl der Iterationen, sowie die Breite und Höhe. Beachte, dass weitaus weniger numPos angegeben werden, als eigentlich vorhanden sind! Das ist notwendig, um etwas Raum für die einzelnen Iterationen zu haben.

Es können noch weitere Parameter übergeben werden, aber diese sind vollkommen ausreichend. Die wichtigsten Werte sind die Anzahl der positiven und negativen Grafiken. Es hat sich als praktikabel erwiesen, ein Verhältnis von 2:1 von positiven:negativen Grafiken zu haben (als allgemeine Faustregel). Daraus erhält man nun die „Stufen“, in unserem Fall 10. Es sollten mindestens 10-20 sein. Je mehr Stufen, desto länger dauert es (die Zeitdauer steigt exponentiell). Das Gute ist, man kann anfangs 10 Stufen trainieren und später mit auf 20 gehen. Dabei werden die vorhandenen Stufen verwendet und dort fortgesetzt, wo das letzte Training beendet wurde. Man könnte theoretisch auch mit 100 Stufen beginnen und über Nacht rechnen lassen. Am nächsten Morgen bricht man das Training ab und schaut, wie weit man gekommen ist. Soll der Befehl ausgeführt werden, wenn das Terminal geschlossen ist, dann kann man nohup nutzen:

nohup opencv_traincascade -data data -vec pos.vec -bg neg.lst -numPos 2000 -numNeg 1000 -numStages 10 -w 20 -h 20 &

Hier nun ein einfaches Testskript, um das trainierte Objekt zu erkennen. Dieses nutzt eine Webcam als Bildeingabe, es lässt sich allerdings auch sehr einfach anpassen, um einfache Bilddateien zu verwenden:

test.py:

import numpy as np
import cv2

# this is the cascade we just made. Call what you want
object_cascade = cv2.CascadeClassifier('data/stage.xml')

cap = cv2.VideoCapture(0)

while 1:
    ret, img = cap.read()
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # image, reject levels level weights.
    objects = object_cascade.detectMultiScale(gray, 50, 50)
    
    for (x,y,w,h) in objects:
        cv2.rectangle(img,(x,y),(x+w,y+h),(255,255,0),2)

    cv2.imshow('img',img)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break

cap.release()
cv2.destroyAllWindows()

Die Größe der Boxen zum Detektieren der Objekte richtet sich nach den Abmessungen der Trainingsdaten. Bei 50 x 50px ergibt sich also eine relativ kleine Box. Bei größeren Abmessungen wie 100×100 sollte das besser funktionieren, allerdings dauert das Training dann auch deutlich länger.

Originalbeitrag in Englisch: pythonprogramming.net
Bildquelle: unsplash.com