This article is a follow-up of the video-to-character animation-python-60 line code, and if interested, see it first.
1. Speed optimization
It's a bit too painful to wait a minute for each play.
So you can use the Pickle module to save the Video_chars, the next time you play, if you find that the current directory has this saved data, skip the conversion, direct playback. That's a lot faster.
Just need to change the test code,
Add two dependencies first at the beginning
import osimport pickle
Then add code at the end of the file:
def dump (obj, file_name): "" "will specify the object with File_nam as the name, save to local" "" with open (file_name, ' WB ') as F:pickle.dump ( obj, f) returndef load (filename): "" "from the specified file in the current folder load Object" "with open (filename, ' RB ') as F:return Pickle.load (f) def get_file_name (File_path): "" Extracts the FileName "" "from the file path without the extension name" "# get the filename from the file path _name path, File_name_w Ith_extension = Os.path.split (file_path) # get the filename prefix file_name, file_extension = Os.path.splitext (file_name_with_exten Sion) return File_namedef has_file (path, file_name): "" "Determines whether there is a file" "" in the specified directory, "" Return file_name in Os.listdir (PATH) def get_video_chars (Video_path, size): "" "returns the video corresponding to the character video" "" Video_dump = Get_file_name (Video_path) + ". Pi Ckle "# If Video_dump already exists in the current folder, it can be read directly in the If Has_file (". ", Video_dump): Print (" Discover the video's conversion cache, direct read ") vid Eo_chars = Load (video_dump) else:print ("Cache not found, start character video conversion") print ("Start frame reading") # video to character animation IMGs = Video2imgs (Video_path, Size) Print ("video is all converted to image, start frame-to-character painting") Video_chars = Imgs2chars (IMGs) print ("Convert complete, start cache result") # Convert Results saved by dump (Video_chars, video_dump) print ("Cache complete") return video_charsif __name__ = = "__main__": # wide, high Size = (64, 48) # Video path, replace with your own Video_path = "Badapple.mp4" Video_chars = Get_video_chars (video_path, size) p Lay_video (Video_chars)
Another optimization method is to play the edge conversion side, which is to perform the above three steps simultaneously. If you learn it, you can try it yourself.
4. Character video and music play simultaneously
There is no soundtrack animation, although it is a very fulfilling feeling, but you may be two times tired of it.
So let's add a soundtrack to it. (Don't worry, you just need to add a few lines of code)
First we need to find a way to play the soundtrack of the video, how to do it?
First introduce a cross-platform video player:MPV, it has a great command line support, please install it first.
To have the MPV play only the music portion of the video, you need only the command:
mpv --no-video video_path
Well, now there is music, but can not also let people open two shells, first put music, and then put character painting it.
At this point, the function we need is to invoke the external app using Python.
But MPV use similar curses function, standard library of Os.system and subprocess can not hide this part, play effect is not satisfactory.
So I used the Pyinvoke module, and as long as I gave it a parameter hide=True
, I could perfectly hide the output of the called program (referring to stdout). Before running the code below, please install the invoke with Pip first. (Can see here, install a module is not a piece of cake)
Okay crap say so much, on the code:
import="BadApple.mp4"invoke.run(f"mpv --no-video {video_path}", hide=True, warn=True)
Run the above test code, if you hear the music, and the shell nothing output, it is normal. We continue.
The music is already there, so it's good to run.
Add a function to play music
import invokedef play_audio(video_path): invoke.run(f"mpv --no-video {video_path}", hide=True, warn=True)
Then modify the main () method:
def main (): # wide, high size = (64 , 48 ) # video path, switch to your own Video_path = # is only converted for 30 seconds, this property is only added, but the previous code is not updated. You may need to go to GitHub to see the latest code. In fact, slightly changed a bit. seconds = 30 # here fps is the frame rate, which is the number of characters played per second 。 For syncing with music. This update is not written in the previous article, please go to GitHub to see the new code. video_chars, fps = get_video_chars (video_path, size, seconds) # play tone Rail Play_audio (video_path) # play video play_video (Video_chars, fps) if __name__ == "__main__" : Main ()
Then run: It's not that I pit you, you only hear the sound, but you don't see the character painting. The reason is: the Invoke.run () function is blocked, the music is not put out, the code will not go to play_video(video_chars, fps)
this line.
So play_audio
change it and change it to this:
import invokefromimport Threaddef play_audio(video_path): def call(): invoke.run(f"mpv --no-video {video_path}", hide=True, warn=True) # 这里创建子线程来执行音乐播放指令,因为 invoke.run() 是一个阻塞的方法,要同时播放字符画和音乐的话,就要用多线程/进程。 = Thread(target=call) p.setDaemon(True) p.start()
This uses the threading of the standard library. Thread class to create a child thread, let the music play in the sub-thread execution, and then the character animation or the main thread execution, Ok, this can see the final effect. In fact, only more than 10 lines of code have been added.
Code:
Video2chars There is a pipfile, because I used the pipenv to manage the reason of dependence. If you don't know it, you can ignore it.
Python video to character painting-advanced