
Out of the shadows
Converted using the cover art created by Dan Luvisi from the novel - Alien: Out of the Shadows written by Tim Lebbon using text from the synopsis over at https://en.wikipedia.org/wiki/Alien:_Out_of_the_Shadows

Converted using the cover art created by Dan Luvisi from the novel - Alien: Out of the Shadows written by Tim Lebbon using text from the synopsis over at https://en.wikipedia.org/wiki/Alien:_Out_of_the_Shadows
I used VLC media player to rip the inital clip out of the director's cut of Alien and then used a combo of an animation splitter/joiner and an art converter, both written in Python/Pygame-CE by me.
I tried ASCII first, but it looked pretty bad, so I switched it up and did 24-bit ANSI instead. Shame though, ASCII for blood has a better ring to it and was what inspired me in the first place, but alas the Reddit compression algorithm butchers them.
Music: Katie Cadet | Old Dance Thing (2014, Public Domain) | loaded as an .xm tracker file.
Image was made in Gimp sometime before 2010 by me, downscaled and converted to 24-bit ANSI before popping it in this.
Frame rate is locked to 120fps with the image scaling up and down in size, two surfaces made of 100 concentric circles blended with the background and bouncing from top to bottom for the first and left to right for the second along a sinewave and then an additional 333 squares, 333 triangles and 333 circles over top, bouncing all over and blended with everything below it.
The end result is a bit chaotic, but it's pretty neat how far you can push Pygame with just a little touch of Numpy.
Simple terminal based image to ANSI converter that loads a text file and an image and prints it out using ANSI escape codes. Adapts to the size of the terminal. Requires OpenCV to be installed.
Change lines 10 and 11 to point to any image or text file you would like to use, by default it looks for them in the same directory that the script is run from.
I tried to keep it under 100 lines and still keep all 16 colour codes in it even though only RED is being used for error messages, so doesn't have much in the way of comments and the spacing is a bit cramped.
If you adapt this class for other uses keep in mind that Ansi.RESET will reset the colour to whatever the default terminal colour is and Ansi.HOME will place the cursor at the top left of the visible terminal.
import shutil
from pathlib import Path
import cv2 # Install with: python -m pip install opencv-python
size = shutil.get_terminal_size()
t_width = size.columns
t_height = size.lines - 1
IMAGE = "doom-1-.gif" # Source: https://doomwiki.org/w/images/4/4b/Doom-1-.gif
TEXT_MSG = "message.txt" # Source: https://www.classicdoom.com/doominfo.htm
class Ansi:
GREY = '\033[90m'
BRIGHT_RED = '\033[91m'
BRIGHT_GREEN = '\033[92m'
BRIGHT_YELLOW = '\033[93m'
BRIGHT_BLUE = '\033[94m'
BRIGHT_MAGENTA = '\033[95m'
BRIGHT_CYAN = '\033[96m'
BRIGHT_WHITE = '\033[99m'
BLACK = '\033[30m'
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[39m'
HOME = '\033[H'
RESET = "\033[0m"
def colour_rgb(pixel_rgb):
r, g, b = pixel_rgb
return f"\033[38;2;{r};{g};{b}m"
def get_dimensions(img_w, img_h, terminal_width, terminal_height):
aspect_ratio = img_h / img_w
new_w = terminal_width
new_h = int((new_w * aspect_ratio) / 2)
if new_h > terminal_height:
new_h = terminal_height
new_w = int((new_h * 2) / aspect_ratio)
return new_w, new_h
def convert_img_to_ansi(img, msg):
if not Path(img).exists():
print(
f"{Ansi.RED}"
f"{img} does not exist."
f"{Ansi.RESET}"
)
return
img = cv2.imread(img)
if img is None:
print(f"{Ansi.RED}Failed to load image {img}{Ansi.RESET}")
return
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_h, img_w = img.shape[:2]
new_w, new_h = get_dimensions(img_w, img_h, t_width, t_height)
resized_img = cv2.resize(
rgb_img, (new_w, new_h),
interpolation=cv2.INTER_AREA
)
lines = []
counter = 0
for y in range(new_h):
current_line = []
for x in range(new_w):
pixel_rgb = resized_img[y, x]
ansi_colour = Ansi.colour_rgb(pixel_rgb)
character = msg[counter % len(msg)]
current_line.append(f"{ansi_colour}{character}")
counter += 1
lines.append("".join(current_line) + Ansi.RESET)
for line in lines:
print(line)
def load_text(text):
if not Path(text).exists():
print(f"{Ansi.RED}{text} does not exist.{Ansi.RESET}")
text = "█"
else:
with open(text, "r", encoding="utf-8") as f:
text = list(f.read().replace("\n", " "))
return text
def main():
text = []
text = load_text(TEXT_MSG)
convert_img_to_ansi(IMAGE, text)
input("")
if __name__ == "__main__":
main()
The original screen recording (using windows built in Snipping Tool) was 2:40 long from start to finish and took up 73.6MB (too big to upload). This version has been split apart, boring wait times and file renaming removed and put back together in the second tool and is now 1:31 and 5.37MB.
Not shown is the python script used to rename the files so that they are in the correct order for merging into an animation (GIF or mp4).
This used to take me an hour or so to do using online tools, but now I can do it in minutes.
The second tool loads mp4s faster than VLC and doesn't stutter like Windows Media Player during playback. It also saves a .wav sound file ripped from any .mp4 that is split but since it isn't intended as a media player I haven't added playing sound or pausing or any of that.
The GIF maker part is still a bit jank, it makes a .txt file with the duration of each frame of any split file, but takes a generic FPS (same as the mp4 maker) instead of giving each frame their own duration in milliseconds like a fully fledged GIF maker would when merging images to GIF.
I've been getting up much too early and going to bed much too late while working on this, I need to take a break I think and get back to my game. I forgot to mention that the art converter does ASCII as well as ANSI conversion, and I have been working on a much simpler mode that just converts everything to coloured "█" blocks which I could always share if anyone's interested.
Original static image sourced from: https://marvelvscapcom.fandom.com/wiki/Iron_Man?file=Iron+Man+-+MvC2.png
Original gifs sourced from: https://www.fightersgeneration.com/characters2/ironman-a.html
Please note: I'm not the original artist! This image was converted from:
The Terminator: Metal #5
Artist: Luke Sparrow
Colors: Colin Craker
I decided to bite the bullet and upload a small game that runs in the rpg/roguelike engine I've been working on for a while to itch called Coolhoolio Johnston's Great Pickle Hunt.
There's not much to it as it is intended to be just for testing but I'd love some feedback on how it run's on other people machines.
*Note: It's a bit too bulky to run in a browser, so download only unfortunately.
The algorithm compressed and added artefacts to this, it looks a much crisper and brighter irl.
I used the screen recorder included with Win11 to record this video and it gave me a 39,973KB .mp4 file. I then used my tool to split it down and rebuild it and ended up with an mp4 file that is 3,779 KB. Of course I could always do this with any number of online tools, but where's the fun in that?
Still a lot of work to do before I'm finished, like converting between formats without splitting first, loading freshly built files to be displayed instead of staying on whatever was being shown previously, loading static image types (I'm only loading animations for now), making different animations from static images with things like cycling hue / saturation, scaling, rotation, etc. Maybe add an option to select and apply watermarks over top.
I need to make the menu slide in when you hover near the edge instead of always over top, and also I'm taking shortcuts by making and displaying everything with an overall fps instead of the seperate duration for each frame but it should be easy enough to change, but tbh I prefer my own jank method for now.
I know my code isn't very optimal but finding out just how slowly it runs when hosted online was kind of a let down tbh.