Python3 video to character animation, the specific code is shown below:
# -*- coding:utf-8 -*- import json import os import subprocess from pathlib import Path from cv2 import cv2 import numpy as np from time import time import webbrowser play_chars_js = ''' let i = 0; (function(){ let img = frames[i++]; let html = "" for(let line of img){ for(let char of line){ let [[r,g,b], ch] = char; html += '<span style="color:rgb(' + r + ', ' + g + ', '+ b + ');">'+ ch + '</span>' // html += '<span style="background-color:rgb(' + r + ', ' + g + ', '+ b + ');">'+ ch + '</span>' } html += "<br>" } ("video-panel")[0].innerHTML = html }, 1000/fps); ("audio")[0].play(); ''' class VideoToHtml: # pixel shapes, because the color is already controlled by rgb, here pixels can actually be randomly arranged pixels = "$#@&%ZYXWVUTSRQPONMLKJIHGFEDCBA098765432?][}{/)(><zyxwvutsrqponmlkjihgfedcba*+1-." def __init__(self, video_path, fps_for_html=8, time_interval=None): """ :param video_path: string, path to video file :param fps_for_html: frame rate of the generated html. :param time_interval: time_interval of the video (start time, end time) in seconds. """ self.video_path = Path(video_path) # Create a VideoCapture object from the specified file. = (video_path) = (cv2.CAP_PROP_FRAME_WIDTH) = (cv2.CAP_PROP_FRAME_HEIGHT) self.frames_count_all = (cv2.CAP_PROP_FRAME_COUNT) = (cv2.CAP_PROP_FPS) self.resize_width = None self.resize_height = None self.frames_count = 0 self.fps_for_html = fps_for_html self.time_interval = time_interval def video2mp3(self): """# Call ffmpeg to get mp3 audio file """" mp3_path = self.video_path.with_suffix('.mp3') ('ffmpeg -i ' + str(self.video_path) + ' -f mp3 ' + str(mp3_path), shell=True) return mp3_path def set_width(self, width): """It can only be reduced and always maintain the aspect ratio""" if width >= : return False else: self.resize_width = width self.resize_height = int( * (width / )) return True def set_height(self, height): """It can only be reduced and always maintain the aspect ratio""" if height >= : return False else: self.resize_height = height self.resize_width = int( * (height / )) return True def resize(self, img): """ Convert img to desired size Principle: only reduce, not enlarge. """ # If you don't specify it, you don't need to resize it. if not self.resize_width or not self.resize_height: return img else: size = (self.resize_width, self.resize_height) return (img, size, interpolation=cv2.INTER_CUBIC) def get_img_by_pos(self, pos): """Get the frame at the specified position.""" # Move the pointer to the position of the specified frame (cv2.CAP_PROP_POS_FRAMES, pos) # () Introduction to return values: # ret Boolean indicating whether the image is read or not # frame is an image matrix of type . ret, frame = () return ret, frame def get_frame_pos(self): """Generate the position of the frame to be acquired, using inert summation.""" step = / self.fps_for_html # If not specified if not self.time_interval: self.frames_count = int(self.frames_count_all / step) # Update count return (int(step * i) for i in range(self.frames_count)) # If specified start, end = self.time_interval pos_start = int( * start) pos_end = int( * end) self.frames_count = int((pos_end - pos_start) / step) # Update count return (pos_start + int(step * i) for i in range(self.frames_count)) def get_imgs(self): assert () for i in self.get_frame_pos(): ret, frame = self.get_img_by_pos(i) if not ret: print("Read failed. Jumped out of loop.") break yield (frame) # Inert summation # Space to be released at the end () def get_char(self, gray): percent = gray / 255 # Converts to between 0-1 index = int(percent * (len() - 1)) # Get the index return [index] def get_json_pic(self, img): """Test phase, not practical.""" json_pic = [] # The width and height are just the opposite of size, mind you. (This is a numpy feature.) height, width, channel = # Converted to grayscale and used to select appropriate characters img_gray = (img, cv2.COLOR_BGR2GRAY) for y in range(height): line = [] for x in range(width): r, g, b = img[y][x] gray = img_gray[y][x] char = self.get_char(gray) ([[int(r), int(g), int(b)], char]) json_pic.append(line) return (json_pic) def write_html_with_json(self, file_name): """Test phase, not practical.""" mp3_path = self.video2mp3() time_start = time() with open(file_name, 'w') as html: # Remember to set the monospace font width or you won't be able to play # ('<!DOCTYPE html>' '<html>' '<body style="font-family: monospace; font-size: small; font-weight: bold; text-align: center; line-height: 0.8;">' '<div class="video-panel"></div>' f'<audio src="{mp3_path.name}" autoplay controls></audio>' '</body>' '<script>' 'var frames=[\n') try: i = 0 for img in self.get_imgs(): json_pic = self.get_json_pic(img) (f"{json_pic},") if i % 20: print(f"degree of progress (on project):{i/self.frames_count * 100:.2f}%, elapsed time:{time() - time_start:.2f}") i += 1 finally: ('\n];\n' f'let fps={self.fps_for_html};\n' f'{play_chars_js}' '</script>\n' '</html>') def main(): # Video path, change it to your own video_path = "ceshi.mp4" video2html = VideoToHtml(video_path, fps_for_html=8) video2html.set_width(120) html_name = Path(video_path).with_suffix(".html").name video2html.write_html_with_json(html_name) if __name__ == "__main__": main()
summarize
The above is a small introduction to the Python3 video to character animation example code, I hope to help you, if you have any questions please leave me a message, I will reply to you in a timely manner. I would also like to thank you very much for your support of my website!
If you find this article helpful, please feel free to reprint it, and please note the source, thank you!