Skip to content

Instantly share code, notes, and snippets.

@heptal
Last active April 23, 2019 13:55
Show Gist options
  • Star 44 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save heptal/6052573 to your computer and use it in GitHub Desktop.
Save heptal/6052573 to your computer and use it in GitHub Desktop.

Displaying images in the terminal with tput and echo

output

Requires ImageMagick, easily available from your favorite package manager. Tested on Linux and OSX
convert image.png -resize 40 txt:-|sed -E 's/://;s/\( ? ?//;s/, ? ?/,/g;s/\)//;s/([0-9]+,[0-9]+,[0-9]+),[0-9]+/\1/g;s/255/254/g;/mage/d'|awk '{print $1,$2}'|sed -E 's/^0,[0-9]+ /print "echo;tput setaf "\;/;s/^[0-9]+,[0-9]+ /print "tput setaf ";/;s/(.+),(.+),(.+)/\1\/42.5*36+\2\/42.5*6+\3\/42.5+16/'|bc|sed 's/$/;echo -n "  ";/'|tr '\n' ' '|sed 's/^/tput rev;/;s/; /;/g;s/$/tput sgr0;echo/'|bash

Old way, with python (cheating):

convert image.png -resize 40 txt:-|sed -E 's/://;s/\( ? ?//;s/, ? ?/,/g;s/\)//;s/(\w,\w,\w),\w/\1/g;/mage/d'|awk '{print $1,$2}'|python -c "import sys;f=sys.stdin.read().split('\n');f=filter(None,f);print 'tput rev;'+''.join([''.join(map(str,('echo;' if x.split(' ')[0].split(',')[0] is '0' else '','tput setaf '+str(sum(p*q for p,q in zip([36,6,1],[int(min(int(c),254)/42.5) for c in x.split(' ')[1].split(',')]))+16)+';echo -n \"  \";'))) for x in f])+'echo;tput sgr0'"|bash

Or, in your profile:

# Display image with tput
function image() {
convert $1 -resize 40 txt:-|sed -E 's/://;s/\( ? ?//;s/, ? ?/,/g;s/\)//;s/([0-9]+,[0-9]+,[0-9]+),[0-9]+/\1/g;s/255/254/g;/mage/d'|awk '{print $1,$2}'|sed -E 's/^0,[0-9]+ /print "echo;tput setaf "\;/;s/^[0-9]+,[0-9]+ /print "tput setaf ";/;s/(.+),(.+),(.+)/\1\/42.5*36+\2\/42.5*6+\3\/42.5+16/'|bc|sed 's/$/;echo -n "  ";/'|tr '\n' ' '|sed 's/^/tput rev;/;s/; /;/g;s/$/tput sgr0;echo/'|bash
}

then, on file or url:
image image.png

If you want to be able to specify a resolution, change image() as follows:
convert "$1" -resize "$2" txt:-...

This one-liner has ImageMagick resize and convert the image into a list of pixel data, which are filtered and piped into inline python ( UPDATE: now without python! ) as a raw list of x,y r,g,b where rgb values are mapped to a terminal color space and then rendered into a string of tput and echo commands, which are finally executed by bash. Note that this violates every conceived notion of efficiency, practicality, readability and maintainability. If you have mission-critical terminal imaging needs, the caca-utils package can probably help.

On OSX, if you use a higher resolution (passing 80 to convert), the sheer number of commands piped to bash may cause a segfault. In that case you need to increase the stack size (ulimit -s 32768) for the session. This issue does not occur on Linux for me even though the default ulimit parameters are the same.
For layered gifs, pass -layers flatten to convert.

I am not responsible if you get fired for using this to dynamically change your boss' MOTD to questionable content.

The colors, which are the 'web-safe' colors, can be viewed here without ImageMagick:

n=6;for r in $(seq 16 $n 231);do echo "$(for c in $(seq $n);do v=$(($r + $c - 1));tput setaf $v && echo -ne $(printf "%03d" $v);echo -n ' '; done)";done

Comparison:

input output

Another fun example, <140 chars (no ImageMagick):

n=16;for r in $(seq 0 $n 255);do echo "$(for c in $(seq $n);do tput setaf $(($r + $c - 1)) && echo -ne '\xE2\x98\x85 ';done)";done

input

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment