Imprimez la tapisserie Game of Thrones sur une imprimante fiscale Ă  l'aide de Python

Une fois, dans l'un des projets, une imprimante fiscale est tombée entre mes mains. Chaque jour, nous rencontrons ces appareils lorsque nous effectuons des paiements dans les magasins, mais peu de gens réalisent ce qu'ils sont vraiment. Je n'entrerai pas dans les détails de leur travail, je dirai simplement que ce sont les choses qui impriment les reçus avec les données d'achat sur du papier thermique spécial (oui, presque toutes les imprimantes fiscales n'ont pas d'encre!).

J'ai dû trouver comment obtenir l'état de fonctionnement de l'imprimante fiscale et ses paramètres internes. La tâche est terminée depuis longtemps, et l'imprimeur fiscal a longtemps été abandonné dans le coin le plus éloigné ... Jusqu'à ce que j'aie l'idée de recréer un peu: D

Ces imprimantes vous permettent d'imprimer des images monochromes. Quand j'ai eu assez de suffisamment de tirages de sceaux, d'emblèmes et de photographies de collègues, j'ai décidé de saluer l'impression d'une longue tapisserie basée sur la série dans laquelle ils tuaient constamment quelqu'un avec les mots "l'hiver arrive" .

La sortie était une telle vidéo:


Étapes détaillées pour l'impression d'une tapisserie en python sous le chat ci-dessous.

Dans cet article, je décrirai brièvement la procédure avec des commentaires détaillés, en supposant que la sortie sera un article de formation avec plusieurs compétences pratiques utiles:

  • tĂ©lĂ©charger la vidĂ©o de tapisserie sur youtube
  • crĂ©er une longue image de tapisserie monochrome pour l'impression sur une imprimante fiscale
  • se connecter Ă  l'imprimante fiscale
  • imprimer une tapisserie sur une imprimante fiscale
  • nous montons le clip vidĂ©o rĂ©sultant pour publication sur les rĂ©seaux sociaux

Téléchargez la vidéo de tapisserie à partir de YouTube


Cela se fait très simplement à l'aide de la bibliothèque pytube , il vous suffit de déterminer l'index du flux vidéo que vous allez télécharger.

Fonction de téléchargement de vidéo depuis 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') 

Lorsque la ligne for stream in streams: print(stream) est exécutée, nous voyons dans la console une liste de tous les flux vidéo contenus dans la vidéo:

 <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 = "128kbps" acodec = "mp4a.40.2">
 <Stream: itag = "249" mime_type = "audio / webm" abr = "50 kbps" acodec = "opus">
 <Stream: itag = "250" mime_type = "audio / webm" abr = "70kbps" acodec = "opus">
 <Stream: itag = "251" mime_type = "audio / webm" abr = "160kbps" acodec = "opus">

J'ai sélectionné le fil avec l'ID 18 car c'est une petite résolution - nous l'imprimons toujours sur une bande de contrôle et téléchargeons plus rapidement))

Créer une longue image de tapisserie monochrome pour l'impression sur une imprimante fiscale


Pour le traitement vidéo, nous avons besoin de la bibliothèque OpenCV bien connue et de Pillow (une branche moderne de PIL) (bien qu'ici, au lieu d'OpenCV, nous pourrions utiliser l'utilitaire avconv de l'outil libav , plus à ce sujet dans la dernière section de cet article). Un grand merci à l'auteur d'avoir écrit python ou python-opencv , qui est une roue python, installée via PIP et ne nécessite pas l'installation d'OpenCV lui-même ( hourra! ).

Une imprimante fiscale ne peut imprimer que des images spéciales - des fichiers bmp monochromes d'une largeur fixe de 528 pixels (mais de longueur illimitée, ho-ho-ho!). De plus, l'image de la tapisserie dans le clip vidéo est constamment en mouvement, nous devons donc couper soigneusement les images afin d'obtenir une longue image.

Tout cela se fait par la fonction suivante:
 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') 


En sortie, on obtient une longue image avec l'image de toute la tapisserie, seul un fragment est montré ci-dessous, l'image originale a une largeur de 55 000 pixels et ne passe pas selon les règles de publication:



Mais une telle image est obtenue après des transformations monochromes, uniquement sans rotation:



Nous imprimons une tapisserie sur une imprimante fiscale


J'ai une imprimante fiscale spécifique du modèle Atol fprint-22 à ma disposition, mais les règles générales s'appliquent aux autres modèles d'imprimantes fiscales. De plus, mon livre fiscal est assez ancien et ne prend pas encore en charge les nouvelles exigences du FZ-54 (je rappelle qu'après l'entrée en vigueur de cette loi, tous les agents fiscaux étaient obligés d'envoyer des données via l'OFD au bureau des impôts, ce qui entraînait des douleurs et des souffrances - un clignotement de chaque appareil).

Une petite digression sur les imprimantes fiscales. Ils concernent les terminaux de point de vente - ce sont toutes sortes de périphériques pour les besoins du commerce, qui sont connectés à un PC et intégrés dans un seul système de comptabilité et de paiement. Parmi ces appareils bien connus, vous avez certainement vu des scanners de codes à barres et des terminaux de paiement par carte de crédit. Pour tous ces appareils, un protocole d'interaction UnifiedPOS unifié a été inventé.

En bref, il s'agit d'un sujet distinct et d'un cercle très restreint de spécialistes impliqués dans les dispositifs POS. La situation est compliquée par le fait que la plupart de ces appareils sont conçus pour fonctionner exclusivement sous Windows via des objets COM - des fichiers dll avec une très mauvaise description documentaire des fonctionnalités. Bien que j'aie entendu parler des systèmes de paiement fonctionnant sous FreeBSD, je n'ai rien vu de tel lorsque je travaillais avec des appareils de point de vente, heureusement, je n'avais pas besoin d'une immersion détaillée dans le monde des processus commerciaux de vente au détail ...

Par conséquent, la procédure pour travailler avec la plupart de ces appareils est la suivante:

  • le pilote est installĂ© par le fabricant
  • la connexion Ă  l'appareil via l'utilitaire du fabricant est configurĂ©e
  • la recherche de la clĂ© de registre souhaitĂ©e avec le pĂ©riphĂ©rique souhaitĂ©
  • la recherche des paramètres nĂ©cessaires pour s'y connecter
    (la plupart fonctionnent sur l'interface logicielle série RS-232)
  • se connecter Ă  l'appareil via l'objet COM du pilote du fabricant
  • travailler avec l'appareil via l'objet COM COM
  • L'objet COM et le port physique de l'appareil sont libĂ©rĂ©s
    ( point important )

Étant donné que j'ai un ancien livre fiscal à ma disposition, le pilote de celui-ci est utilisé spécifiquement pour la 8e version. Maintenant, le fabricant a ajouté un pilote de la 10e version, ce qui simplifie considérablement le travail avec une imprimante fiscale via un module d'emballage python distinct, ce qui est une bonne nouvelle.

Le code suivant illustre une fonction qui se connecte à une imprimante fiscale à l'aide de l'algorithme décrit ci-dessus, produit un bip et imprime l'image de tapisserie monochrome précédemment obtenue. Nous devrons installer pywin32 .

Le code s'est avéré assez long et ennuyeux, alors je l'ai mis sous le spoiler:
 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') 


La sortie était un tel manuscrit, une performance sur "Un chant de glace et de feu":



L'imprimante a finalement hurlé, imprimé lentement et faiblement, puis complètement terminé d'imprimer uniquement la scène finale - pas une seule imprimante fiscale n'a jamais vu une telle charge: D

Reste à préparer la vidéo et à la poster sur le réseau social. En tant que série audio , j'ai trouvé une composition amateur sur le réseau 8 bits - le thème du titre de la série . L'idée était de se superposer sans utiliser un éditeur vidéo, j'écrirai à ce sujet plus tard dans la dernière partie de l'article.

Nous montons le clip vidéo résultant pour publication sur les réseaux sociaux


À ces fins, il existe un outil de console très utile et puissant qui remplace tout l'éditeur vidéo - libav . Il comprend les utilitaires avconf et ffmpeg pour travailler avec des fichiers vidéo et audio. Honnêtement, pour moi cet outil a été une vraie découverte, je le recommande à tout le monde!

L'idée de base de l'installation:

  • coupe de la vidĂ©o tournĂ©e sur la partie smartphone au dĂ©but et Ă  la fin
    (pour adapter un fichier mp3 avec de la musique 8 bits pour 3 pièces)
  • Ă©crire 3 pertes de fichiers audio dans un fichier
  • superposer un fichier vidĂ©o et un fichier audio dans un nouveau fichier vidĂ©o
  • convertir un fichier vidĂ©o du format mov au format mp4
    (mon smartphone tourne des vidéos avec l'extension mov)

À ces fins, j'ai écrit un script à exécuter sur la ligne de commande, qui peut être exécuté à la fois bash sous linux et bat dans win (les différences sont indiquées dans les commentaires du script):
 #     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 


C'est tout, la vidéo est créée:


PS: mon premier article sur Habré, prévu d'écrire un court article pour commencer, coupé autant que je pouvais) J'espère que la lecture a été agréable, et le résultat de mon travail - intéressant)

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


All Articles