Drucken Sie mit Python einen Wandteppich Game of Thrones auf einem Steuerdrucker

Einmal fiel mir in einem der Projekte ein Steuerdrucker in die Hände. Jeden Tag begegnen wir diesen Geräten, wenn wir in Geschäften bezahlen, aber nur wenige Menschen erkennen, was sie wirklich sind. Ich werde nicht auf Details ihrer Arbeit eingehen, ich möchte nur sagen, dass dies die Dinge sind, die Quittungen mit Kaufdaten auf speziellem Thermopapier drucken (ja, fast alle Steuerdrucker haben keine Tinte!).

Ich musste herausfinden, wie ich den Funktionszustand des Steuerdruckers und seine internen Einstellungen ermitteln konnte. Die Aufgabe ist schon lange erledigt und der Steuerdrucker wurde für eine lange Zeit in der hinteren Ecke aufgegeben ... Bis ich auf die Idee kam, ein wenig nachzubauen: D.

Mit solchen Druckern können Sie monochrome Bilder drucken. Als ich genug von genügend Drucken von Siegeln, Emblemen und Fotos von Kollegen hatte, beschloss ich, dem Druck eines langen Wandteppichs zu winken, der auf der Serie basiert, in der sie ständig jemanden mit den Worten „Winter ist nahe“ getötet haben .

Die Ausgabe war so ein Video:


Detaillierte Schritte zum Drucken eines Wandteppichs in Python unter der Katze unten.

In diesem Artikel werde ich das Verfahren kurz mit detaillierten Kommentaren beschreiben, vorausgesetzt, die Ausgabe ist ein Schulungsartikel mit mehreren nützlichen praktischen Fähigkeiten:

  • Laden Sie das Tapisserie-Video von YouTube herunter
  • Erstellen Sie ein langes monochromes Wandteppichbild zum Drucken auf einem Steuerdrucker
  • Stellen Sie eine Verbindung zum Steuerdrucker her
  • Drucken Sie einen Wandteppich auf einem Steuerdrucker
  • Wir mounten den resultierenden Videoclip zur Veröffentlichung in sozialen Netzwerken

Laden Sie das Tapisserie-Video von YouTube herunter


Dies geschieht ganz einfach mit der Pytube- Bibliothek. Sie müssen lediglich den Index des Videostreams ermitteln, den Sie herunterladen möchten .

Funktion zum Herunterladen von Videos von YouTube:
import time, pytube #         def load_bmp_from_video(video_url, filename): t1 = time.clock() #     video = pytube.YouTube(video_url) #        streams = video.streams.all() for stream in streams: print(stream) #       ,  18: 360p mp4 video.streams.get_by_itag(18).download("./", filename = filename ) t2 = time.clock() #      print('download done', t2-t1) #       got.mp4   360x640 load_bmp_from_video(video_url = 'https://www.youtube.com/watch?v=aZV4PclhHeA&', filename = 'got') 

Wenn die Zeile for stream in streams: print(stream) ausgeführt wird, wird in der Konsole eine Liste aller im Video enthaltenen Videostreams angezeigt:

 <Stream: itag = "22" mime_type = "video / mp4" res = "720p" fps = "30fps" vcodec = "avc1.64001F" acodec = "mp4a.40.2">
 <Stream: itag = "43" mime_type = "video / webm" res = "360p" fps = "30fps" vcodec = "vp8.0" acodec = "vorbis">
 <Stream: itag = "18" mime_type = "video / mp4" res = "360p" fps = "30fps" vcodec = "avc1.42001E" acodec = "mp4a.40.2">
 <Stream: itag = "137" mime_type = "video / mp4" res = "1080p" fps = "30fps" vcodec = "avc1.640028">
 <Stream: itag = "248" mime_type = "video / webm" res = "1080p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "136" mime_type = "video / mp4" res = "720p" fps = "30fps" vcodec = "avc1.4d401f">
 <Stream: itag = "247" mime_type = "video / webm" res = "720p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "135" mime_type = "video / mp4" res = "480p" fps = "30fps" vcodec = "avc1.4d401e">
 <Stream: itag = "244" mime_type = "video / webm" res = "480p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "134" mime_type = "video / mp4" res = "360p" fps = "30fps" vcodec = "avc1.4d401e">
 <Stream: itag = "243" mime_type = "video / webm" res = "360p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "133" mime_type = "video / mp4" res = "240p" fps = "30fps" vcodec = "avc1.4d4015">
 <Stream: itag = "242" mime_type = "video / webm" res = "240p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "160" mime_type = "video / mp4" res = "144p" fps = "30fps" vcodec = "avc1.4d400c">
 <Stream: itag = "278" mime_type = "video / webm" res = "144p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "140" mime_type = "audio / mp4" abr = "128 kbps" acodec = "mp4a.40.2">
 <Stream: itag = "249" mime_type = "audio / webm" abr = "50 kbps" acodec = "opus">
 <Stream: itag = "250" mime_type = "audio / webm" abr = "70 kbps" acodec = "opus">
 <Stream: itag = "251" mime_type = "audio / webm" abr = "160 kbps" acodec = "opus">

Ich habe Thread mit ID 18 ausgewählt, weil es ist eine kleine Auflösung - wir drucken sie immer noch auf ein Scheckband und laden sie schneller herunter))

Erstellen Sie ein langes monochromes Wandteppichbild zum Drucken auf einem Steuerdrucker


Für die Videoverarbeitung benötigen wir die bekannte OpenCV- Bibliothek und Pillow (eine moderne Abzweigung von PIL) (obwohl wir hier anstelle von OpenCV das Dienstprogramm avconv aus dem libav- Tool verwenden könnten, mehr dazu im letzten Abschnitt dieses Artikels). Vielen Dank an den Autor für das Schreiben von Python oder Python-OpenCV , einem Python-Rad, das über PIP installiert wurde und keine Installation von OpenCV selbst erfordert ( Hurra! ).

Ein Steuerdrucker kann nur spezielle Bilder drucken - monochrome BMP-Dateien mit einer festen Breite von 528 Pixel (aber unbegrenzte Länge, ho-ho-ho!). Außerdem bewegt sich das Gobelinbild im Videoclip ständig, sodass wir die Rahmen sorgfältig ausschneiden müssen, damit wir ein langes Bild erhalten.

All dies wird durch die folgende Funktion erledigt:
 import os, cv2, numpy as np from PIL import Image #            def save_frames_from_vide(filename): #          real_filename = filename.rsplit('.', 1)[0] #         for file in os.listdir('./'): if file.startswith('frame'): os.remove('./' + file) #         frames_list = [] vidcap = cv2.VideoCapture(filename) try: success, frame = vidcap.read() count = 1 while success: # and count < 500: #    #     1  100,     if count in [1, 100, 30945, 31000] or count % 370 == 0: #      (  ) mono_frame = frame if count == 370: mono_frame = mono_frame[0:mono_frame.shape[0], 172:mono_frame.shape[1]] if count == 30710: mono_frame = mono_frame[0:mono_frame.shape[0], 0:mono_frame.shape[1] - 200] mono_frame = mono_frame[20:-20, :] frames_list.append(mono_frame) print('read a new frame: ', success, count) success, frame = vidcap.read() count += 1 finally: vidcap.release() #     gobelin = np.concatenate((frames_list), axis = 1) #   -    cv2.imwrite('%s.png' % real_filename, gobelin) #        image = Image.open('%s.png' % real_filename) #  1        bmp fn = lambda x : 255 if x > 135 else 0 image = image.convert('L').point(fn, mode = '1') #       528  coef = 528. / image.height new_w = int(image.width * coef) new_h = int(image.height * coef) image = image.resize((new_w, new_h)) #    270         image = image.transpose(Image.ROTATE_270) image.save('%s_for_print.bmp' % real_filename) #       'got.png'  'got_for_print.bmp' save_frames_from_vide('got.mp4') 


Bei der Ausgabe erhalten wir ein langes Bild mit dem Bild des gesamten Wandteppichs. Unten ist nur ein Fragment dargestellt. Das Originalbild hat eine Breite von 55.000 Pixel und entspricht nicht den Veröffentlichungsregeln:



Ein solches Bild wird jedoch nach monochromen Transformationen nur ohne Rotation erhalten:



Wir drucken einen Wandteppich auf einem Steuerdrucker


Ich habe einen bestimmten Steuerdrucker des Atol fprint-22-Modells zur Verfügung, aber die allgemeinen Regeln gelten für andere Steuerdruckermodelle. Darüber hinaus ist mein Finanzbuch ziemlich alt und unterstützt noch nicht die neuen Anforderungen von FZ-54 (ich erinnere mich, dass alle Finanzbeamten nach Inkrafttreten dieses Gesetzes verpflichtet waren, Daten über das OFD an das Finanzamt zu senden, was Schmerzen und Leiden mit sich brachte - ein Aufblitzen jedes Geräts).

Ein kleiner Exkurs über Steuerdrucker. Sie beziehen sich auf POS-Geräte - dies sind alle Arten von Peripheriegeräten für den Handel, die an einen PC angeschlossen und in ein einziges Buchhaltungs- und Zahlungssystem integriert sind. Von diesen bekannten Geräten haben Sie definitiv Barcode-Scanner und Kreditkartenzahlungsterminals gesehen. Für alle diese Geräte wurde ein einheitliches UnifiedPOS-Interaktionsprotokoll erfunden.

Kurz gesagt, dies ist ein separates Thema und ein sehr enger Kreis von Spezialisten für POS-Geräte. Die Situation wird durch die Tatsache kompliziert, dass die meisten dieser Geräte für den Betrieb ausschließlich unter Windows über COM-Objekte ausgelegt sind - DLL-Dateien mit einer sehr schlechten dokumentarischen Beschreibung der Funktionalität. Obwohl ich von den unter FreeBSD laufenden Cash-Systemen gehört habe, habe ich bei der Arbeit mit POS-Geräten so etwas noch nicht gesehen. Zum Glück brauchte ich kein detailliertes Eintauchen in die Welt der POS-Geschäftsprozesse im Einzelhandel ...

Daher ist das Verfahren zum Arbeiten mit den meisten dieser Geräte wie folgt:

  • Treiber wird vom Hersteller installiert
  • Die Verbindung zum Gerät über das Dienstprogramm des Herstellers ist konfiguriert
  • Suche nach dem gewünschten Registrierungsschlüssel mit dem gewünschten Gerät
  • Suche nach den notwendigen Verbindungsparametern dazu
    (Die meisten arbeiten an der seriellen RS-232-Softwareschnittstelle.)
  • Stellen Sie über das COM-Objekt des Treibers des Herstellers eine Verbindung zum Gerät her
  • Arbeiten mit dem Gerät über das API-COM-Objekt
  • Das COM-Objekt und der physische Port des Geräts werden freigegeben
    ( wichtiger Punkt )

Da mir ein altes Finanzbuch zur Verfügung steht, wird der Treiber dafür speziell für die 8. Version verwendet. Jetzt hat der Hersteller einen Treiber der 10. Version hinzugefügt, der die Arbeit mit einem Steuerdrucker über ein separates Python-Wrapper-Modul erheblich vereinfacht. Dies ist eine gute Nachricht.

Der folgende Code zeigt eine Funktion, die mithilfe des oben beschriebenen Algorithmus eine Verbindung zu einem Steuerdrucker herstellt, einen Piepton erzeugt und das zuvor erhaltene monochrome Wandteppichbild druckt. Wir müssen pywin32 installieren.

Der Code erwies sich als ziemlich lang und langweilig, also habe ich ihn unter den Spoiler gestellt:
 import win32com.client from _winreg import HKEY_CURRENT_USER, OpenKey, EnumValue #         class FiscallError(Exception) #     COM  , #  COM     def fiscal_print(filename): driver = None try: #      #         8.16. try: hKey = OpenKey(HKEY_CURRENT_USER, r"Software\Atol\Drivers\8.0\KKM\Devices") except Exception as err: raise FiscallError('      ' + '  ' + '   FPrint22-') #       , #       com  try: device_name,device_connect_params,device_connect_dt=EnumValue(hKey,0) except Exception as err: raise FiscallError('     ' + '     ' + '  FPrint22-') #       try: connect_dir = dict([tup.split(u'=') for tup in device_connect_params]) except Exception as err: raise FiscallError('     ' + '    FPrint22-') #    COM  try: driver = win32com.client.Dispatch("AddIn.FPrnM8") except Exception as err: raise FiscallError(' COM  AddIn.FPrnM8   ' + ' FPrint22-    , ' + '    ') #         add_code = driver.AddDevice() if driver.ResultCode != 0: raise FiscallError('     FPrint22-' + ' [ %s] - %s'% (driver.ResultCode, driver.ResultDescription)) #       COM  driver.Model = connect_dir['Model'] driver.PortNumber = connect_dir['PortNumber'] driver.UseAccessPassword = connect_dir['UseAccessPassword'] driver.DefaultPassword = connect_dir['UseAccessPassword'] driver.PortNumber = connect_dir['PortNumber'] driver.BaudRate = connect_dir['BaudRate'] #         #       COM  driver.DeviceEnabled = 1 #         GetStatus, #       . 61    v8.0 res = driver.GetStatus() if driver.ResultCode != 0: raise FiscallError('     FPrint22- ' + '[ %s] - %s' % (driver.ResultCode, driver.ResultDescription)) ###   #  ,       #   ,       driver.Beep() #  ,      (528) print('driver.PixelLineLength:', driver.PixelLineLength) # !!!       # (    COM ) driver.Alignment = 0 driver.LeftMargin = 0 driver.PrintPurpose = 1 driver.AutoSize = False driver.Scale = 100 #      driver.FileName = filename #    bmp  driver.PrintBitmapFromFile #        10  for i in range(10): driver.PrintString() #      driver.FullCut() # ! except FiscallError as err: raise err except Exception as err: raise FiscallError('    ' + '   FPrint22- - %s' % str(err)) finally: if driver: driver.DeviceEnabled = 0 fiscal_print('got_for_print.bmp') 


Das Ergebnis war ein solches Manuskript, eine Aufführung zu „Ein Lied von Eis und Feuer“:



Der Drucker am Ende heulte vollständig, druckte langsam und schwach und druckte dann nur die letzte Szene vollständig aus - kein Steuerdrucker hat jemals eine solche Belastung gesehen: D.

Es bleibt, das Video vorzubereiten und im sozialen Netzwerk zu posten. Als Audioserie fand ich eine Amateurkomposition im 8-Bit- Netzwerk - das Titelthema der Serie . Die Idee war, sich gegenseitig zu überlagern, ohne einen Video-Editor zu verwenden. Ich werde später im letzten Teil des Artikels darüber schreiben.

Wir stellen den resultierenden Videoclip zur Veröffentlichung in sozialen Netzwerken bereit


Für diese Zwecke gibt es ein sehr nützliches und leistungsstarkes Konsolentool, das den gesamten Video-Editor ersetzt - libav . Es enthält die Dienstprogramme avconf und ffmpeg für die Arbeit mit Video- und Audiodateien. Ehrlich gesagt, für mich war dieses Tool eine echte Entdeckung, ich kann es jedem empfehlen!

Die Grundidee der Installation:

  • Ausschnitt aus dem auf dem Smartphone aufgenommenen Video am Anfang und am Ende
    (passend für MP3-Datei mit 8-Bit-Musik für 3 Spiele)
  • Schreiben Sie 3 Audiodateiverluste in eine Datei
  • Überlagern Sie Videodateien und Audiodateien in eine neue Videodatei
  • Konvertieren Sie die Videodatei vom Mov-Format in das MP4-Format
    (Mein Smartphone nimmt Videos mit der Erweiterung mov auf.)

Zu diesem Zweck habe ich ein Skript für die Befehlszeile geschrieben, das sowohl unter Linux als auch unter Win ausgeführt werden kann (die Unterschiede sind in den Skriptkommentaren angegeben):
 #     avconv -ss 00:00:10 -i got_print.mov -t 00:06:00 -c:v copy got_print_tmp.mov #      ( win) (echo file 'got_8bit.mp3' & echo file 'got_8bit.mp3' & echo file 'got_8bit.mp3') > list.txt #      ( linux) # cat list.txt # file 'got_8bit.mp3' # file 'got_8bit.mp3' # file 'got_8bit.mp3' #     3  ffmpeg -f concat -i list.txt -acodec copy got_8bit_3.mp3 #         avconv -i got_print_tmp.mov -i got_8bit_3.mp3 -c copy got_print_final.mov #       mp4 avconv -i got_print_final.mov -c:v libx264 got_print_final.mp4 #    (windows) del got_8bit_3.mp3 del got_print_tmp.mov #    (linux) # rm got_8bit_3.mp3 # rm got_print_tmp.mov 


Das ist alles, das Video wird erstellt:


PS: Mein erster Artikel über Habré, der zunächst einen kurzen Artikel schreiben sollte, schnitt so viel wie möglich ab. Ich hoffe, dass die Lektüre angenehm und das Ergebnis meiner Arbeit interessant war.

Source: https://habr.com/ru/post/de465485/


All Articles