Code Monkey home page Code Monkey logo

bad-apple's Introduction

bad-apple

Bad Apple printed out on the console with Python!

Bad apple!!

Preface

A word of disclaimer, while the final code is somewhat original, this project is an amalgamation of different code snippets that I found online. As the main YouTube Video begins to gain traction, I feel the need to inform the audience that this code is NOT ENTIRELY ORIGINAL.

The concept of playing Bad Apple!! on a Command Line Interface (CLI) is not a novel idea and I am definitely not the first.

There are many iterations and versions around YouTube and I wanted to give it a shot. The intent of posting the video on YouTube was to show a few friends of a simple weekend project that I whipped up in Python.

My own video can be found here.

Running this code/Pre-requisites

Thanks to TheHusyin for adding a requirements.txt file for easier installs.

You can either git clone or download a ZIP of this repository.

git clone https://github.com/CalvinLoke/bad-apple

Then, ensure that you set your terminal to the directory of this repository.

cd bad-apple

Install the necessary dependencies and packages by using:

pip install -r requirements.txt

And to run the code:

python touhou_bad_apple_v4.0.py

And just follow the on-screen prompts.

Performance optimizations

Currently, my implementation of a rudimentary static time.sleep() function results in an incremental error over time. This thus leads to the frame accuracy drifting.

UPDATE on 22/04/21

With the replacement of the playsound library with pygame, the error over time seemed to have been fixed. Though, further improvements and optimizations to the code can still be done. As of current, performance is still not optimal. A major bottleneck lies in the IOPS when dealing with the .txt files, am still trying to find a better implementation.

I am also looking into improving frame extraction and generation times.

UPDATE ON 23/04/21

It seems that frame extraction is heavily bottle-necked by the drive's IOPS, and adding threads did not seem to expediate frame extraction further. I have created some rudimentary code for process-based and threading-based frame extraction, am looking to implement it for the ASCII generation soon.

UPDATE ON 25/04/21

I got about to implementing multi-processing for both frame extraction and ASCII generation. Though it seems that my implementation of threading/processing is still very botchy and thus asset generation is still sub-optimal. Not too sure on how far I would want to take this project, though my main priority right now would be to adjust frame timings.

SECOND UPDATE ON 25/04/21

Simply by replacing the primitive time.sleep() function with the fpstimer library, frame-time accuracy has been drastically improved, will be slowing down my code optimizations for playback from now onwards.

Though the main concern right now is trying to optimize asset generation times.

THIRD UPDATE ON 25/04/21

Changed the approach of storing assets. Should significantly reduce asset generation times, averaging arond 10 ~ 15 seconds using single thread. Will still look into threading to further expedite asset generation. However, with touhou_bad_apple_v4.0.py, progress will now slow down as I finally close the chapter of this project.

UPDATE ON 27/04/21

It looks like most of the issues have been rectified, and the code has reached desirableh performance. While I could further boost ASCII generation and add new functionality to the code, I feel that it would be over-engineering such a simple project. What started out as a weekend project blew up to such proportions, and led me to learn many new and interesting concepts along the way.

I really would like to thank JasperTecHK for his recommendations and suggestions along the way. His input was what really led me to return to this project after two dead weeks.

As such, major updates to the code would come much slower now, as the current iteration of the project has far exceeded my orginal goal. Though, it would be interesting to further develop v4.5 to have color support, but I would presume that requires its own development cycle. Once again, I really would like to thank all the contributors to this simple and dumb piece of code that I wrote in 24 hours.

Cheers!

Current known issues and bugs

Despite being a somewhat simple program, my crappy implementation has led to a lot of unresovled bugs and issues. I am currently looking at fixing some of them.

  1. block=False is not supported in Linux (Only for v2.0 and below)

I am currently trying to find alternatives to the playsound library. Using two different threads is not an option currently as I was running into desynchronization issues

This issue has been fixed in v3 v2.5, alongside other performance improvements.

  1. No such file or directory: 'ExtractedFrames/BadApple_1.jpg' (Only for v3.0 and v2.5)

Not really sure how this is happening, but will be looking into fixing it. I was unable to replicate the error but I assume it is due to my botchy implementation of file directories for the assets

Issue could be due to host machine not having ffmpeg installed. Ensure that you have ffmpeg installed and run the script again. v4 and v4.5 will not return this error, though will need to do some limit testing to figure it out.

Version descriptor

  1. touhou_bad_apple_v1.py

First rudimentary version that accomplishes basic frame extraction and animation. Utilizes threads, but suffers from heavy synchronization issues.

  1. touhou_bad_apple_v2.py

Extended version that includes a "GUI", some basic file I/O. Suffers from slight synchronization issues. Core program logic was completed in 24 hours with some minor tweaks and comments afterwards.

  1. touhou_bad_apple_v3.py ==> Renamed to touhou_bad_apple_v2.5.py

Current development version. Improved frame time delay and better file I/O. Looking to implement threading to expedite frame extraction and ASCII conversion. Play-testing version to use py-game. Doesn't really warrant a full version increment, will be updating the name to v2.5 or something like that once the new v4 is ready

Slightly better version due to incorporation of pygame for music playing. Rectifies issue when attempting to play on Linux based environments since the older playsound library did not support blocking=False on said environments.

Still has rudimentary frame extraction and ASCII generation on single thread/process, which makes asset generation significantly longer.

  1. touhou_bad_apple_v4.py ==> Renamed to touhou_bad_apple_v3.0.py

(Almost) re-written as the previous code was getting to messy to work with. Functions from previous versions are still used though.

Will be renamed to v3 once I improve asset generation times with better threading code. However, "v4" is currently the most frame-accurate version thanks to the fpstimer library. And subsequent changes are only for smaller performance optimizations.

Rewritten to incorporate multiprocess, though implementation is very janky. Overall program structure was also refactored a bit to clean up main() function. Asset generation times were reduced a bit, but the double for loop meant that it generation times are close to a minute.

  1. touhou_bad_apple_v4-5.py ==> Renamed to touhou_bad_apple_v4.0.py

Once again my dumb naming schemes kick in again. After some toying around, I decided to scrap the .txt file generation and skip right storing ASCII within memory. This version completely rewrites the asset generation algorithm. Instead of the old

Video => Extracted_Images (stored in storage) => ASCII Characters (stored in memory) => .txt (stored in storage)

process, ASCII generation is done on the image stored within memory, so

Video => Extracted_Images (stored in memory) => ASCII Characters (stored in memory) => Internal list (stored in memory)

Makes more sense as compared to older iterations and significantly cuts down asset generation times.

While this means that 10 or 20 seconds is required for ASCII generation, it eliminates storage IO bottleneck. Also frees up a lot of storage space on host system. Overall probably the best one yet?

  1. touhou_bad_apple_v5.py ==> Renamed to touhou_bad_apple_v4.5.py

Honestly I should not even get a job at file versioning. This version essentially allows the user to ASCII-fy any video provided that they have the video file in the root directory.

Functions

The main functions will be listed here.

play_video()

Reads the files from the previously generated ASCII .txt files and prints it out onto the console.

play_audio()

Plays the bad apple audio track.

progress_bar(current, total, barLength=25)

A simple progress bar function that generates the status of both frame extraction and ASCII frame generation. This code was taken from a StackOverflow thread.

current is the current value/progress of the process.

total is the desired/intended end value of the process.

barLength=25 sets the length of the progress bar. (Default is 25 characters)

ASCII Frame generation

Not a particular function, but a group of functions.

resize_image()

greyscale()

pixels_to_ascii()

These functions are called in the ascii_generator() function to convert image files to ASCII format and stores them into .txt files.

Note that the ASCII conversion code is not original, and was taken from here.

Words of acknowledgements

I should give credit where credit is due, and here is a section dedicated to that.

ZUN, and this incredible work on the Touhou project over the past decades.

Alstroemeria Records, for making the original Bad Apple!! MV.

Ronald Macdonald, for making the MIDI Arrangement of the Bad-Apple!! used.

GitHub users karoush1, JasperTecHK, TheHusyin, Mirageofmage for their comments and bugfixes.

bad-apple's People

Contributors

calvinloke avatar huseyinhealth avatar mirageofmage avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

bad-apple's Issues

V4.5 works flawless on fedora but:

At the end, theres that. :I
it works perfectly until it reaches the end
Python 3.9 has been used.
Afaik Python 3.10 has some issues cause this hasn't been optimized for Python 3.10 or newer.

Traceback (most recent call last):
  File "/home/hax/GitHub/bad-apple/touhou_bad_apple_v4.5.py", line 181, in <module>
    main()
  File "/home/hax/GitHub/bad-apple/touhou_bad_apple_v4.5.py", line 171, in main
    play_video(total_frames=total_frames)
  File "/home/hax/GitHub/bad-apple/touhou_bad_apple_v4.5.py", line 37, in play_video
    sys.stdout.write("\r" + ASCII_LIST[frame_number])
IndexError: list index out of range 

Not really an issue, just some tweaks, from a new-ish scripter

Forked your repo, and had a look, also had a look about the suggestions made by the other user, but honestly, cv2 and the way you're currently doing the ascii conversion, is over my head slightly. If I properly sat down and read it, I might get somewhere, but anyways.
I've tweaked your script to also use threading on the frames, so it should be faster. Also, completely removed the need to save to a file, twice, by just directly passing the cv2 result to PIL, and then outputting the frames, though I do like the idea for keeping the frames, which is why I'm also looking into perhaps RLE compressing the frames into a single file, instead of having a whole folder of 7kb ASCIIs.
I'm not really sure if you wanted the tweaks I made in your version, so I made it as a separate file on my repo and put this as a issue, instead of a PR (though if comments were a thing, I'd put it there), if you just want to take a look. Just as someone who vaguely knows Python, this was a nice project to at least be able to work on.

Oh, and progress bar still works!

Possible optimizations in ASCII generation, video quality, and video playback

Hi, I saw the code on touhou_bad_apple_v2.py and I though of some possible speed improvements right off the bat.

First is ASCII generation. As you're doing right now, the best way to improve processing speed is by creating a Pool and allocating each frame to a worker until the frame extraction is over. The disadvantage is that you lose the current progress bar you have implemented, and instead will need something more complex like an N lines progress bar, one for each worker in the pool, or a signaling method between threads, like a semaphore, and is just too much work for what the current project is aiming for. You can also improve the video quality by adding more characters to the list, more specifically a space(all black) " " and a block character(all white) "\u2588", and adapting the padding calculation on line 86 to include the new characters.

Another improvement, and probably the most important one, is saving the ASCII frames to a single file containing all frames, instead of multiple files. The reasoning is that opening and closing files takes time, which would be better spent synchronizing the frames in the terminal.

Checking files also takes time. As of right now, you are manually checking every single .txt file for all the frames and checking if they exist. This can be done much faster if you use instead os.listdir() and filter the results containing the string .txt, then count the results(or a single file if you use the approach above). Also, please always use os.path.join() instead of manually using /. Reasoning is that the os.path library provides cross-platform separators and better handling of absolute paths, which is much less error prone.

I could make a fork and do those changes, but I think it would be very useful if you did them yourself, since you can learn new skills and functions with them. Tell me what you think about my suggestions, then close this issue later.

Edit: my mistake, I just noticed you changed to a white background in the video. Correct characters are a space(all white) " " and a block character(all black) "\u2588"

block=False is not supported in Linux

block=False is not supported in Linux, you may need to add an thread to execute playsound():

import threading
...

threading.Thread(target=playsound, args=("tng_bad-apple.mp3",)).start()

[touhou_bad_apple_v4.5.py] pygame.error: Unrecognized audio format

Hey :) Its been a while since my last ticket :)
I have a new one for yaa. Even when this project was a weekend project. im still amazed about your progress..

It seems to be that there is something wrong with the audio format...

see log:

python3 touhou_bad_apple_v4.5.py

Select option:

  1. Play
  2. Exit
    ==============================================================
    Your option: 1
    Please enter the video file name (file must be in root!): BadApple.mp4
    MoviePy - Writing audio in audio.mp3
    MoviePy - Done.
    Beginning ASCII generation...
    Progress: [####################### ] 99% Frame 6570 of 6571 framesASCII generation completed! ASCII generation time: 28.546977519989014Traceback (most recent call last):
    File "touhou_bad_apple_v4.5.py", line 181, in
    main()
    File "touhou_bad_apple_v4.5.py", line 170, in main
    play_audio('audio.mp3')
    File "touhou_bad_apple_v4.5.py", line 24, in play_audio
    pygame.mixer.music.load(path)
    pygame.error: Unrecognized audio format

ModuleNotFOundError: No module named 'PIL'

I never used Python but I really wanted to try this. I followed all the steps.

\Desktop\bad-apple-master\touhou_bad_apple_v3.py", line 7, in
from PIL import Image
ModuleNotFoundError: No module named 'PIL'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.