Ghytro

ascii_video.py

Feb 16th, 2022 (edited)
1,115
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.05 KB | None | 0 0
  1. import argparse, cv2, threading, time, os
  2. from queue import Queue
  3.  
  4.  
  5. ASCII_FRAME_WIDTH = 80
  6. ASCII_MAX_FRAME_HEIGHT = 38
  7. INIT_BUFFER_SIZE = 200
  8. CLEANER_SYMBOL = "\033[F"
  9. cleaner_str = ""
  10.  
  11.  
  12. # the frame should be grayscaled
  13. def frame_to_ascii(frame):
  14.     return "\n".join(["".join([pixel_to_ascii(c) for c in row]) for row in frame])
  15.            
  16.  
  17. def pixel_to_ascii(pixel):
  18.     if pixel <= 12:
  19.         return ' '
  20.     if pixel <= 50:
  21.         return '.'
  22.     if pixel <= 75:
  23.         return '*'
  24.     if pixel <= 100:
  25.         return '='
  26.     if pixel <= 125:
  27.         return '|'
  28.     if pixel <= 150:
  29.         return 'J'
  30.     if pixel <= 200:
  31.         return 'P'
  32.     return 'B'
  33.  
  34.  
  35. def show_ascii_frame(printable_frame):
  36.     print(printable_frame, end=cleaner_str)
  37.  
  38.  
  39. # frames consumer transforms grayscale image to ascii and prints it from frames queue
  40. def frames_consumer(frames_queue, frame_pause):
  41.     while True:
  42.         if frames_queue.empty():
  43.             continue
  44.         show_ascii_frame(frames_queue.get())
  45.         frames_queue.task_done()
  46.         time.sleep(frame_pause)
  47.  
  48.  
  49. def main():
  50.     global cleaner_str
  51.  
  52.     parser = argparse.ArgumentParser(description="A tool to play any video as an ascii animation")
  53.     parser.add_argument("source", type=str, help="A path to the video that's needed to be played")
  54.     args = parser.parse_args()
  55.     video_path = args.source
  56.     cap = cv2.VideoCapture(video_path)
  57.     if not cap.isOpened():
  58.         parser.error("Cannot initialize video capture from a given file, path to a file may be incorrect")
  59.  
  60.     # calculating scaled size of frame
  61.     init_frame_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
  62.     init_frame_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
  63.     scale_koef = ASCII_FRAME_WIDTH / init_frame_width
  64.     scaled_width = ASCII_FRAME_WIDTH
  65.     scaled_height = min(int(init_frame_height * scale_koef), ASCII_MAX_FRAME_HEIGHT)
  66.     cleaner_str = CLEANER_SYMBOL * (scaled_height - 1)
  67.  
  68.     # initializing queue needed variables
  69.     fps = cap.get(cv2.CAP_PROP_FPS)
  70.     frame_pause = 1 / (fps * 2)
  71.     frames_queue = Queue()
  72.     total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
  73.     frames_counter = 0
  74.     consumer_started = False
  75.     buffering_frames = min(INIT_BUFFER_SIZE, total_frames)
  76.  
  77.     while cap.isOpened():
  78.         ret, frame = cap.read()
  79.         if not ret:
  80.             break
  81.         frames_counter += 1
  82.         frame = cv2.resize(frame, (scaled_width, scaled_height), interpolation=cv2.INTER_AREA)
  83.         frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  84.         frames_queue.put(frame_to_ascii(frame))
  85.         if not consumer_started and frames_counter >= buffering_frames:
  86.             threading.Thread(target=frames_consumer, args=(frames_queue, frame_pause,), daemon=True).start()
  87.             consumer_started = True
  88.         elif not consumer_started:
  89.             print(f"Initial bufferization: frame {frames_counter}/{buffering_frames}", end='\r')
  90.  
  91.     while not frames_queue.empty():
  92.         time.sleep(.1)
  93.     os.system("cls")
  94.     exit()
  95.  
  96. if __name__ == "__main__":
  97.     main()
  98.  
Advertisement
Add Comment
Please, Sign In to add comment