Imprimir tapiz Game of Thrones en una impresora fiscal usando Python

Una vez, en uno de los proyectos, una impresora fiscal cayó en mis manos. Todos los días nos encontramos con estos dispositivos cuando hacemos pagos en las tiendas, pero pocas personas se dan cuenta de lo que realmente son. No entraré en detalles de su trabajo, solo diré que estas son las cosas que imprimen recibos con datos de compra en papel térmico especial (sí, ¡casi todas las impresoras fiscales no tienen tinta!).

Tuve que descubrir cómo obtener el estado de funcionamiento de la impresora fiscal y su configuración interna. La tarea se completó hace mucho tiempo, y la impresora fiscal fue abandonada durante mucho tiempo en la esquina más alejada ... Hasta que se me ocurrió la idea de recrear un poco: D

Estas impresoras le permiten imprimir imágenes monocromas. Cuando tuve suficientes impresiones de sellos, emblemas y fotografías de colegas, decidí saludar a la impresión de un largo tapiz basado en la serie en la que constantemente mataban a alguien con las palabras "se acerca el invierno" .

La salida fue tal video:


Pasos detallados para imprimir un tapiz en python debajo del gato a continuación.

En este artículo, describiré brevemente el procedimiento con comentarios detallados, asumiendo que el resultado será un artículo de capacitación con varias habilidades prácticas útiles:

  • descarga el video del tapiz de youtube
  • crear una imagen de tapiz monocromática larga para imprimir en una impresora fiscal
  • conectarse a la impresora fiscal
  • imprimir un tapiz en una impresora fiscal
  • Montamos el video clip resultante para su publicación en las redes sociales

Descarga el video del tapiz de youtube


Se hace de manera muy simple usando la biblioteca de pytube , solo necesita determinar el índice de la transmisión de video que va a descargar.

Función para descargar videos de 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') 

Cuando se ejecuta la línea for stream in streams: print(stream) , vemos en la consola una lista de todas las secuencias de video contenidas en el video:

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

Seleccioné hilo con id 18 porque es una resolución pequeña, todavía la imprimimos en una cinta de verificación y la descargamos más rápido))

Cree una imagen de tapiz monocromática larga para imprimir en una impresora fiscal


Para el procesamiento de video, necesitamos la conocida biblioteca OpenCV y Pillow (una bifurcación moderna de PIL) (aunque aquí en lugar de OpenCV uno podría usar la utilidad avconv de la herramienta libav , más sobre esto en la última sección de este artículo). Muchas gracias al autor por escribir python o python-opencv , que es una rueda de python, instalada a través de PIP y no requiere la instalación de OpenCV ( ¡hurra! ).

Una impresora fiscal solo puede imprimir imágenes especiales: archivos bmp monocromos de un ancho fijo de 528 píxeles (pero longitud ilimitada, ¡ho-ho-ho!). Además, la imagen de tapiz en el videoclip se mueve constantemente, por lo que debemos cortar cuidadosamente los cuadros para obtener una imagen larga.

Todo esto se realiza mediante la siguiente función:
 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 la salida, obtenemos una imagen larga con la imagen de todo el tapiz, solo se muestra un fragmento a continuación, la imagen original tiene un ancho de 55,000 píxeles y no pasa de acuerdo con las reglas de publicación:



Pero dicha imagen se obtiene después de transformaciones monocromas, solo sin rotación:



Imprimimos un tapiz en una impresora fiscal


Tengo una impresora fiscal específica del modelo Atol fprint-22 a mi disposición, pero las reglas generales se aplican a otros modelos de impresora fiscal. Además, mi libro fiscal es bastante antiguo y aún no es compatible con los requisitos novedosos del FZ-54 (recuerdo que después de la entrada en vigor de esta ley, todos los funcionarios fiscales estaban obligados a enviar datos a través de la OFD a la oficina de impuestos, lo que implicaba dolor y sufrimiento, un parpadeo de cada dispositivo).

Una pequeña digresión sobre impresoras fiscales. Se relacionan con los dispositivos POS: se trata de todo tipo de periféricos para las necesidades del comercio, que están conectados a una PC e integrados en un único sistema de contabilidad y pago. De estos dispositivos conocidos, definitivamente ha visto escáneres de códigos de barras y terminales de pago con tarjeta de crédito. Para todos estos dispositivos, se inventó un protocolo unificado de interacción UnifiedPOS.

En resumen, este es un tema separado y un círculo muy estrecho de especialistas involucrados en dispositivos POS. La situación se complica por el hecho de que la mayoría de estos dispositivos están diseñados para funcionar exclusivamente bajo Windows a través de objetos COM: archivos dll con una descripción documental muy pobre de la funcionalidad. Aunque escuché acerca de los sistemas de efectivo que se ejecutan en FreeBSD, no he visto nada así mientras trabajaba con dispositivos POS, afortunadamente, no necesitaba una inmersión detallada en el mundo de los procesos comerciales de POS de Retail ...

Por lo tanto, el procedimiento para trabajar con la mayoría de estos dispositivos es el siguiente:

  • el controlador está instalado por el fabricante
  • la conexión al dispositivo a través de la utilidad del fabricante está configurada
  • buscando la clave de registro deseada con el dispositivo deseado
  • buscando la configuración necesaria para conectarse
    (la mayoría funciona en la interfaz de software serie RS-232)
  • conectarse al dispositivo a través del objeto COM del controlador del fabricante
  • trabajando con el dispositivo a través del objeto COM API
  • Se liberan el objeto COM y el puerto físico del dispositivo
    ( punto importante )

Como tengo un antiguo libro fiscal a mi disposición, el controlador para él se usa específicamente para la octava versión. Ahora el fabricante ha agregado un controlador de la décima versión, lo que simplifica enormemente el trabajo con una impresora fiscal a través de un módulo de envoltura de Python separado, lo cual es una buena noticia.

El siguiente código muestra una función que se conecta a una impresora fiscal utilizando el algoritmo descrito anteriormente, produce un pitido e imprime la imagen de tapiz monocromática obtenida previamente. Necesitaremos instalar pywin32 .

El código resultó ser bastante largo y aburrido, así que lo puse debajo del 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') 


El resultado fue un manuscrito, una actuación en "Una canción de hielo y fuego":



La impresora al final aulló por completo, imprimió lenta y tenuemente, y luego terminó por completo de imprimir solo la escena final; ninguna impresora fiscal ha visto tal carga: D

Queda por preparar el video y publicarlo en la red social. Como serie de audio , encontré una composición de aficionados en la red de 8 bits, el tema principal de la serie . La idea era superponerse entre sí sin usar un editor de video, escribiré sobre esto más adelante en la parte final del artículo.

Montamos el video clip resultante para su publicación en las redes sociales


Para estos fines, existe una herramienta de consola muy útil y potente que reemplaza todo el editor de video: libav . Incluye utilidades avconf y ffmpeg para trabajar con archivos de video y audio. Honestamente, para mí esta herramienta fue un verdadero descubrimiento, ¡se la recomiendo a todos!

La idea básica de instalación:

  • corte del video grabado en la parte del teléfono inteligente al principio y parte al final
    (para ajustar archivos mp3 con música de 8 bits para 3 reproducciones)
  • escribir 3 pérdidas de archivos de audio en un archivo
  • superponer archivo de video y audio en un nuevo archivo de video
  • convertir archivos de video de formato mov a formato mp4
    (mi teléfono inteligente graba videos con la extensión mov)

Para estos fines, escribí un script para ejecutar en la línea de comando, que se puede ejecutar tanto en bash en linux como en bat en win (las diferencias se indican en los comentarios del 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 


Eso es todo, el video está creado:


PD: mi primer artículo sobre Habré, planeado escribir un breve artículo para empezar, recorté todo lo que pude) Espero que la lectura haya sido agradable y el resultado de mi trabajo, interesante.

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


All Articles