Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ws2122:asciia:ascii-animation.py [2022/03/16 15:05] Jaden |
ws2122:asciia:ascii-animation.py [2022/03/30 08:23] (aktuell) MoscarTU |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
===== ASCII-Animation.py ===== | ===== ASCII-Animation.py ===== | ||
+ | Diese Datei ist die Hauptdatei unseres Programms ASCIÌ-Animation. Sie muss ausgeführt werden, um das Programm zu starten. Neben der Main-Methode werden noch einige Hilfsfunktionen, unter anderem für eine kleine Benutzeroberfläche, implementiert. | ||
<code python> | <code python> | ||
import cv2 # muss extra installiert werden mit: pip install opencv-python | import cv2 # muss extra installiert werden mit: pip install opencv-python | ||
Zeile 11: | Zeile 11: | ||
import pyfiglet # muss extra installiert werden mit: pip install pyfiglet | import pyfiglet # muss extra installiert werden mit: pip install pyfiglet | ||
import colorama | import colorama | ||
- | from colorama import Fore, Back, Style # Diese und folgende Zeile für Farbänderung | + | from colorama import Fore, Back, Style |
colorama.init() | colorama.init() | ||
import threading | import threading | ||
Zeile 19: | Zeile 19: | ||
abh.Abbruchsignal() | abh.Abbruchsignal() | ||
</code> | </code> | ||
- | An dieser Stelle werden lediglich externe Bibliotheken und unsere Hilfsdateien importiert. | + | An dieser Stelle werden lediglich externe Bibliotheken und unsere Hilfsdateien importiert. Beachtet werden muss nur, dass cv2 (opencv), readchar und pyfiglet standardmäßig nicht bei Python installiert sind. |
<code python> | <code python> | ||
- | def Eingabepruefung(pruefe, ArtZahl): # Methode für korrekte Eingabe der Ja/Nein Eingaben, ArtZahl ist für welche Eingabe gemeint ist (beliebig erweiterbar) | + | # Methode für korrekte Eingabe der Ja/Nein Abfragen, ArtZahl ist für welche Eingabe gemeint ist (beliebig erweiterbar) |
+ | def Eingabepruefung(pruefe, ArtZahl): | ||
while True: | while True: | ||
if pruefe == "j" or pruefe == "n": | if pruefe == "j" or pruefe == "n": | ||
Zeile 36: | Zeile 37: | ||
pruefe = readchar.readkey().lower() | pruefe = readchar.readkey().lower() | ||
- | def FarbAnalyse(Farbe): # Methode für korrekte Eingabe der Farben | + | # Methode für korrekte Eingabe der Farben |
+ | def FarbAnalyse(Farbe): | ||
while True: | while True: | ||
if Farbe == "BLACK" or Farbe == "RED" or Farbe == "GREEN" or Farbe == "YELLOW" or Farbe == "BLUE" or Farbe == "MAGENTA" or Farbe == "CYAN" or Farbe == "WHITE": | if Farbe == "BLACK" or Farbe == "RED" or Farbe == "GREEN" or Farbe == "YELLOW" or Farbe == "BLUE" or Farbe == "MAGENTA" or Farbe == "CYAN" or Farbe == "WHITE": | ||
return Farbe | return Farbe | ||
else: | else: | ||
- | Farbe = input("Das war keine gültige Farbe. Gib erneut eine Farbe ein: ").upper() # Methode 1 bis 3 (also für korrekte Eingabe) vielleicht in extra Datei auslagern <<<<<<<<<--------------------------------- | + | Farbe = input("Das war keine gültige Farbe. Gib erneut eine Farbe ein: ").upper() |
</code> | </code> | ||
- | Diese Methoden prüfen, ob die Nutzereingaben gültig sind und verlangen bei Ungültigkeit neue Eingaben. | + | Diese Methoden prüfen, ob die Nutzereingaben (zu Farbauswahl, Farben, Invertierung) gültig sind und verlangen bei Ungültigkeit neue Eingaben. |
<code python> | <code python> | ||
- | def LangsamesDrucken(text): # Methode für langsames Drucken der Buchstaben | + | # Methode für langsames Drucken der Buchstaben |
+ | def LangsamesDrucken(text): | ||
for a in text: | for a in text: | ||
print(a, end='') | print(a, end='') | ||
Zeile 51: | Zeile 54: | ||
time.sleep(0.08) | time.sleep(0.08) | ||
</code> | </code> | ||
- | Darstellung des Intro-Textes | + | Die Methode ist für das langsame Ausgeben der Buchstaben vom Intro-Text zuständig (Schreibmaschinenstil). |
<code python> | <code python> | ||
def BilderAusVideo(): # Wird mit neuem Thread gleichzeitg zum Konvertieren ausgeführt | def BilderAusVideo(): # Wird mit neuem Thread gleichzeitg zum Konvertieren ausgeführt | ||
Zeile 69: | Zeile 72: | ||
break | break | ||
</code> | </code> | ||
- | Diese Funktion sorgt dafür, dass das Generieren der ASCII-Frames und das Abspielen der schon berechneten Frames gleichzeitig passieren kann. | + | Diese Funktion ist für das Speichern der Frames aus dem Video zuständig. Weiter unten wird ein zusätzlicher Thread für die Methode ausgeführt, sodass das Umwandeln der Frames zu ASCII und die anschließende Ausgabe gleichzeitig passieren kann. |
<code python> | <code python> | ||
# main | # main | ||
Zeile 101: | Zeile 104: | ||
vorFarbe = "WHITE" | vorFarbe = "WHITE" | ||
hinFarbe = "BLACK" | hinFarbe = "BLACK" | ||
- | abh.AbbruchFarbeZuweisung (vorFarbe, hinFarbe) # Übertrag der Farbwerte zu Abbruch (für globale Variable) | + | |
+ | # Übertrag der Farbwerte zu Abbruch (für globale Variable) | ||
+ | abh.AbbruchFarbeZuweisung (vorFarbe, hinFarbe) | ||
</code> | </code> | ||
- | Anfang der Main-Methode, Einlesen der Datei, Abfrage der veränderlichen Eigenschaften (Invertierung, Farbwahl) | + | Das ist der Anfang der Main-Methode. Hier ist eine Benutzeroberfläche für das Einlesen der Videodatei und eine Abfrage der veränderlichen Eigenschaften (Invertierung, Farbwahl). |
<code python> | <code python> | ||
ordnerName = 'Bildercache' | ordnerName = 'Bildercache' | ||
- | if os.path.exists(ordnerName): # Fehlerabfang | + | if os.path.exists(ordnerName): |
- | shutil.rmtree(ordnerName) # Löscht kompletten Ordner | + | shutil.rmtree(ordnerName) |
- | os.makedirs(ordnerName) # Erstellt Ordner | + | os.makedirs(ordnerName) |
- | + | </code> | |
- | # Einlesen des Videos und Infos | + | Der Datei erstellt den Ordner für die Bilder, mit einem Fehlerabfang, falls dieser schon existiert. |
- | cap = cv2.VideoCapture(videoname) # oder 'shadowanimation2.mp4' | + | <code python> |
+ | cap = cv2.VideoCapture(videoname) | ||
anzahlFrames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | anzahlFrames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | ||
fps = int(cap.get(cv2.CAP_PROP_FPS)) | fps = int(cap.get(cv2.CAP_PROP_FPS)) | ||
- | #print (anzahlFrames) | ||
- | abc.UebertrageZuConverter(fps, booleanInvertierung) # übertragt FPS und Variable für Invertierung oder nicht (für globale Variable) | + | # übertragt FPS und Variable für Invertierung oder nicht (für globale Variable) |
+ | abc.UebertrageZuConverter(fps, booleanInvertierung) | ||
- | t1 = threading.Thread(target=BilderAusVideo, args=()) # Thread für fast gleichzeitiges Ausführen von Bilder aus Video und Convertieren | + | # Thread für fast gleichzeitiges Ausführen von Bilder aus Video und Konvertieren |
+ | t1 = threading.Thread(target=BilderAusVideo, args=()) | ||
t1.start() # Thread Start | t1.start() # Thread Start | ||
+ | </code> | ||
+ | Hier werden die Videoinformationen verarbeitet und gespeichert. Außerdem startet hier der zusätzliche Thread mit der Methode "BilderAusVideo" (siehe oben). Dadurch passiert der Teil ab hier bis unten zum "t1.join()" parallel zu der Methode aus dem Threading. | ||
+ | <code python> | ||
zeit = 0 | zeit = 0 | ||
- | while (zeit <= 100): # kleine Ladeschleife (für Laden der Anfangsbilder) | + | while (zeit <= 100): |
print("Loading...",zeit,"%", end="\r") | print("Loading...",zeit,"%", end="\r") | ||
zeit += 1 | zeit += 1 | ||
time.sleep(0.05) # 0.05 -> 5 Sekunden warten | time.sleep(0.05) # 0.05 -> 5 Sekunden warten | ||
- | #time.sleep(10) # jetzt sind es 5 Sekunden | + | </code> |
+ | Hier ist ein Ladebalken für die Vorlaufszeit, damit erst ein paar Frames aus dem Video genommen werden, bevor das Umwandeln zu ASCII passiert. | ||
+ | <code python> | ||
+ | # für Hintergrundfarbe und Zeichenfarbe | ||
+ | print(getattr(Fore, vorFarbe) + getattr(Back, hinFarbe), end="\r") # getattr ändert char/string in Attribut | ||
- | print(getattr(Fore, vorFarbe) + getattr(Back, hinFarbe), end="\r") # für Hintergrundfarbe und Zeichenfarbe, print(Style.RESET_ALL) für Reseten | + | # Wandelt frames in ASCII um, bis alle Dateien umgewandelt wurden |
- | # getattr ändert char/string in Attribut | + | for i in range (anzahlFrames): |
- | for i in range (anzahlFrames): # Wandelt frames in ASCII um, bis alle Dateien umgewandelt wurden | + | |
start = timeit.default_timer() | start = timeit.default_timer() | ||
abc.Umwandlung(i) | abc.Umwandlung(i) | ||
ende = timeit.default_timer() | ende = timeit.default_timer() | ||
dauer = ende - start # Messung der Berechnungszeit für die Umwandlung | dauer = ende - start # Messung der Berechnungszeit für die Umwandlung | ||
- | pause = 1/fps - (dauer + 0.003) # größere Zahl -> Video wird schneller abgespielt | + | pause = 1/fps - (dauer + 0.003) |
if pause < 0: | if pause < 0: | ||
pause = 0 | pause = 0 | ||
Zeile 142: | Zeile 155: | ||
t1.join() # Thread Ende bei erfolgreichen Abschließen der Schleifen | t1.join() # Thread Ende bei erfolgreichen Abschließen der Schleifen | ||
- | shutil.rmtree('Bildercache') # Löscht kompletten Cacheordner | + | shutil.rmtree('Bildercache') # Löscht kompletten Bildercache |
print(Style.RESET_ALL) | print(Style.RESET_ALL) | ||
- | |||
</code> | </code> | ||
+ | Die for-Schleife geht alle Frames durch und ruft jedes mal die Datei "AsciiBildConverter" auf, in der ein "normales" Bild zu einem ASCII-Bild umgewandelt wird. Durch die for-Schleife werden die Bilder sehr schnell umgewandelt und auf der Konsole hintereinander ausgegeben, wodurch es wie ein Video aussieht. Dabei haben wir noch einen Timer implementiert, welcher die Zeit misst, damit die Videowiedergabe annähernd wie die vom Originalvideo ist. Allerdings ist das immer noch nicht optimal, weil die Berechnung dafür auch Mikrosekunden benötigt. | ||
+ | Am Programmende wird der Cache mit den Bildern gelöscht. | ||
+ | |||
+ | Tipp: Bei einer Pause oder am Ende des Programms die Anaconda hochscrollen, dadurch wird sichtbar, dass es wie ein "Daumenkino" ist. |