Primal Interpunkt Asciifier

An edge-based approach to ASCII-art generation
 J---------------------------------------------------------------------------------o  
 |                                                                                 |  
 |                                                                 ----            |  
 |      |----|            |---|  |--------------_^^         ^_-----T-------_^V     |  
 |      |    |            |   |  |     |---^^o    -^\    ^^^^   ^^^--  --^^^//     |  
 |      |    |            |   |  |     |     \      |   ^^    ^^T          ^/      |  
 |      |    |            |   |  |     |     /     ^/  /Y    /Y                    |  
 |      |    |            |   |  |     L---^^'--_^^'  A/    o/                     |  
 |      |    |            |   |  |     L-----^--_-^^  |     L                      |  
 |      |    |            |   |  |     |    ^\     ^\-Vo    'A                     |  
 |      |    L-           |   V  |     |     '|      | \     \\                    |  
 |      \     \^         ^/ ^X   |     |     /V     /V \\^    \^^           -_     |  
 |      \^^    ^^^-----_^^^^^    |     |--_^^^    ^^^    ^^^L   ^^^__  -_^^^/'     |  
 |        '^__----_-----^^^      L-------------__^^         ^^_------------_/      |  
 |                '                                                                |  
 |         ___----------__-_L     _-__---------____L     _-_-----------_--_        |  
 |      |--T                ------T                ------T                ---|     |  
 |      |     _-----------           _----------^           __---------_     |     |  
 |      L----^'          -^----------           -----------^           --_---J     |  
 |         __------------___      __-_---VAXA--__-_      __-------------__L        |  
 |      |---                -A \\--     |^JoT      --/ Y--                ---|     |  
 |      |     ----------_L   VoA\o   ---JAYoL--_L   // V    -----------^     |     |  
 |      L-----T          ^-_--AV\\_--T   | |    --_X/V J---^T          ^-----J     |  
 |         --_-------A^J\-__   V\\---_--J| |L----_^//V   -__^Y^//------__-         |  
 |      |---         \\\\\ T--V'A\\\   /JT |'A   ///JVV--- ^Y///          ---|     |  
 |      |     _-------A\\\\\  oAT \\\--A J YoL--/// AJV   ^oX//--------_     |     |  
 |      L-----+         X\\\---AV  \--  |'  |   -/  |A---X^Y/'         ^-----J     |  
o|            -^        \AT\\   VA  \   |   |   /Y  V   JYY//        ^Y            |  
||             \^^       \- \\   A  '\  |   |  /Y  AV  o^  /       ^^Y             |oA
|LA             \\^^      A  \\  T   \\ J   L //   A  ^o  /Y     ^^^Y             JJX 
| A              \\T^^_   VA  T\oV    \\J   'J/    VJYY  //    ^^ToY              A | 
V A     \^^       \\  ^^^  \    \\o    J     Y    LVY    /  J^^T oY       J^Y     V V 
VAVA    T\^^^^     \\   ^^^ X    \J               AY    //^^^   oY     ^^^^/Y    /VAV 
 V A      \\ ^^^^   \\    ^^^A                         o/^^    oY   ^^^^  YY     / /  
 VA\\      \\   ^^^^ \\     ^^                         /^     oY ^^^^   ^Y'     ////  
  \-\o      \\     -^^\\                                     Y/^^^     oX      o/ /   
   \ \o      \\       ++                                     ^-       ^Y      JY /    
   T\ \\      \Y^                                                    ^o      oY YY    
    T\^\\       \^                                                 ^^'      ^YJYY     
      \\\^       \^^                                             ^^^       ^Y^X       
       \\'^^       ^^^                                         ^^^       ^^Y^o        
        '^^^^^       ^^^                                     ^^^       ^^^^^Y         
          \^^^^^       +^^^                               ^^^T       ^^^^^^           
            \^^^^^^       -^^^^                       -^^^-       J^^^^^^             
              '^^^^^^         ^^^_^               -^^^^         ^^^^^^T               
                 ^^^^^^^^         T--____   _-___--         ^^^^^^^^                  
                    ^^^^^^^^-           -----           ^^^^^^^^^                     
                        ^^--^^^_-_                 J_^^^-^^^^                         
                            -^_-__---___     --_---^_-^--                             
                                  --_-------_---_-^                                   
                                        '\oY'                                         
(Generated from this source image)

Overview
Typically, tools to generate ASCII art from raster images, such as aalib or libcaca, quantize the input image to a series of brightness levels, and a printable ASCII character is then assigned to a corresponding subregion of the image, where a "darker" brightness level is assigned a "denser" character, and lighter brightness levels are assigned "sparser" characters. While simple to implement and fast enough for real-time purposes (both mplayer and VLC can display video as ASCII, and Quake has been modified to run in the terminal), it is difficult to see what the image is unless the characters are very small; additionally, ANSI colour information is often needed in order to improve the "shading" of the image.

One can observe that more information than simply "brightness" can be encoded in an ASCII character; in particular, certain characters such as slashes, dashes, pipes, and other punctuation marks suggest strong direction. Also, the choice of character itself could suggest fine detail within the image (ie. a '+' when two near-parallel lines intersect, a 'O' for small circular features, etc.) Additionally, large swatches of similar colours look very busy to the eye; therefore, we may only want to register characters for regions of the image that correspond to changes in intensity; in other words, it is fruitful to consider an edge-based approach to ASCII-art generation.

Methodology
Therefore, my CPSC 505 miniproject, "Primal Interpunkt", performs Canny edge-detection on an input in order to extract the primal sketch of an image. Additionally, each subregion of the Canny primal sketch is treated as a filter, and template matching is applied to a subset of printable ASCII characters in order to ask the question, "what character best approximates this region of interest?" The character that corresponds to the highest normalized cross corellation with the filter is outputted.

Initially, the entire range of printable ASCII characters were used. However, this resulted in a very noisy output. Therefore, a subset was chosen by hand that perhaps best represents the kind of edges we would like to capture in text. Different characters could be chosen to represent different "shading types", at the artistic whim of the user.

Results
Input Parameters Output Notes
test1.png - Rosie 100x35 chars, 4000, 2000 test1.txt Canny grabs a bit of the darker patches of grass, but changing Canny parameters causes more detail in the subject to be lost. Uniform brightness in the fur makes the final ASCII image rather uninteresting visually.
test2.png - ntaylor 110x55 chars, 1000, 1200 test2.txt Loss of detail around eye area, larger output required to separate glasses from eyebrows. Additionally, subject needs a shave.
test3.tif - wheel 80x30 chars, 5500, 4200 test3.txt Bob's favourite picture!
test4.jpg - obama 90x60 chars, 4000, 3500 test4.txt
test5.jpg - picasso 150x60 chars, 4000 2000 test5.txt Doesn't turn out well even at a high "textual resolution"; however, the Canny primal sketch is itself almost unintelligable (that is, we lose a great deal along with shading)
test6.jpg - final exam 100x40 chars, 4000 2000 test6.txt If you only look at one example, look at this one.
test7.jpg - Stockholm 200x70 chars, 3000 1000 test7.txt "Panoramic" shot - hard to destinguish texture of buildings, even at a high-resolution. However, an interesting effect is seen in how the water's reflection is handled.

Ideas for future improvement Sadly, we are not able to obtain real-time generation. However, Andrej Karpathy wisely points out that hashing image subregions as we encounter them would drastically increase performance. Additionally, an oft-encountered probem was that certain regions of the image required very different parameters to the Canny edge detection than other parts of the image, resulting in a compromise that results in the quality of the entire image suffering. It might be prudent to consider a scaled representation of the primal sketch(es). On the other hand, the algorithm lends itself nicely to parallelization. Therefore, an implementation in a shader language such as HLSL might prove to be interesting for real-time graphics effects, an analogue to the aalib Quake project.

Download

XCode project (zipped; requires OpenCV installed in /System/Library)