⬆️ ⬇️

Convert image to ANSI

I do not know how interesting this will be to someone, but the other day I decided to play around and do the following:



Given: Picture (for example, BMP) 640 by 400, font 8 by 16



Necessary: ​​Translate it into ANSI pseudographics in standard 80 by 25 characters, characters and background can be of any color (true color).

')

image



Thanks to very good comments, I updated the version: link

With it you get something like this: image





It is worth mentioning here that the “true” EGA text mode with 16 colors will not suit us, as it will turn out too ugly due to the low resolution and large size of characters. The second argument: the task is not to bring the picture as close as possible to its symbolic representation, since then it would be possible to reduce the font, increase the resolution, etc. The task - to make just such a filter, which is described above.



For what I publish it here: it seemed to me that it would be useful for novice programmers to practice understanding the algorithm, so I marked this material as a tutorial.



To implement, we will need some kind of library for working with graphics, which can getpixel and putpixel, that is, read the color of the pixel from the screen and draw the pixel with the specified color on the screen. I use an old but proven Allegro library (www.allegro.cc), which is easy to use and also allows you to work in different operating systems (even in MSDOS!).



But enough words, it's time to work.



const int size_x=640; const int size_y=400; const int ascii_x=80; const int ascii_y=25; 




Set the constants responsible for the size of the image and the size of its textual representation.



 for(int cx=0;cx<ascii_x;cx++) for(int cy=0;cy<ascii_y;cy++){ int fullr=0; int fullg=0; int fullb=0; int pixels=0; //find background color for(int i=0;i<size_x/ascii_x;i++) for(int j=0;j<size_y/ascii_y;j++){ int col=getpixel(pic,i+cx*size_x/ascii_x,j+cy*size_y/ascii_y); int r=getr(col); int g=getg(col); int b=getb(col); fullr+=r;fullg+=g;fullb+=b; pixels++; } fullr/=pixels;fullg/=pixels;fullb/=pixels; 




We start the main loop in which we sequentially find each character from 80 to 25 matrices. The next cycle is for each pixel of that area of ​​the picture that corresponds to the given symbol from this matrix on the screen. As it is easy to figure out, this area is 640/80 = 8 by 400/25 = 16 pixels. Here it is useful to remember that this is exactly equal to the size of our font.



In this second cycle, our task is to find the average color, which we will set as the background color of our ANSI character. It is very simple to do this: we spread the color on the R, G, B components and calculate the average for each of them.



 //find ascii and its color int rms=100000000; for(int curch=1;curch<256;curch++){ //loop over ASCII table int charr=0; int charg=0; int charb=0; //find char color pixels=0; //number of pixels for char for(i=0;i<size_x/ascii_x;i++) for(int j=0;j<size_y/ascii_y;j++){ int col=getpixel(pic,i+cx*size_x/ascii_x,j+cy*size_y/ascii_y); int r=getr(col); int g=getg(col); int b=getb(col); int colch=getpixel(font,i+(curch%32)*8,j+(curch/32)*16); int rc=getr(colch); int gc=getg(colch); int bc=getb(colch); if(rc!=0&&gc!=0&&bc!=0){ //get actual char pixels only charr+=r; charg+=g; charb+=b; pixels++; } } if(pixels!=0){ charr/=pixels;charg/=pixels;charb/=pixels; } 




Next is more interesting. We have to insert another cycle - for each character from the ASCII table (there are 256 of them). I take them from a black and white picture with a font, that is, theoretically, there can be any 8 by 16 images as a symbol. It should be noted that in this place you can limit yourself to only one area of ​​characters (for example, Cyrillic), which will greatly affect the result.



In the above code snippet we find the color of the symbol. To do this, we consider the amount as above, but with a slight change: the pixels are taken according to the mask, which is the current character from the font. Thus, the second “average color” corresponds only to the pixels with which the symbol itself will be drawn.



It is also worth noting that the number of such pixels can be zero (for example, the symbol 32 is a space). With this you need to be careful.



 //find rms int currms=0; for(i=0;i<size_x/ascii_x;i++) for(int j=0;j<size_y/ascii_y;j++){ int col=getpixel(pic,i+cx*size_x/ascii_x,j+cy*size_y/ascii_y); int r=getr(col); int g=getg(col); int b=getb(col); int rr=0,gg=0,bb=0; int colch=getpixel(font,i+(curch%32)*8,j+(curch/32)*16); int rc=getr(colch); int gc=getg(colch); int bc=getb(colch); if(rc!=0&&gc!=0&&bc!=0){ //char pixel rr=charr; gg=charg; bb=charb; } else{ //back pixel rr=fullr; gg=fullg; bb=fullb; } currms+=sqrt((r-rr)*(r-rr)+(g-gg)*(g-gg)+(b-bb)*(b-bb)); } if(currms<rms){ //find minimal rms findr=charr; findg=charg; findb=charb; findch=curch; rms=currms; if(DEBUG)printf("!!!%d %d\n",currms, curch); } else if(DEBUG)printf("%d %d\n",currms, curch); } //find char 




Well, finally, they came to the salt itself. In this piece of code, we once again traverse the pixels of the specified image area and assume the RMS (standard deviation) of all three color components from the background color or symbol (depending on whether the pixel is on the background or on the symbol). Total RMS and will be a criterion of how well this symbol fits well with this area of ​​the picture.



This is followed by the output to the screen (in two modes - with the background and without ( example )), but we will not dwell on it, since everything is self-evident.



image



The program and source code can be downloaded here: dimouse.ru/data/ansiconv.rar (130 Kb).



How about drawing this thing in text mode? As you can see, the program creates a file with the results (except for the new bmp) - ascii. There for each character its value is stored in the ASCII table, its color and the color of its background. I use pdcurses.sourceforge.net for text mode (write in the comments if you know something better!), But this library, although it is a wrapper over SDL (in any case, the Windows version is for sure) cannot display colors anymore 16: heavy legacy of antiquity. As you probably remember, PDCurses originates from the famous Interactive Fiction game Curses . But one friend wrote an improvement to this library and posted it here: www.projectpluto.com/win32a.htm



Unfortunately, it is impossible to use the bitmap font in it, only the system font (but you can choose any), which spoils the impression a bit. But the task can also be considered completed.



A program for reading ascii can be downloaded here: dimouse.ru/data/ascii.rar (70 Kb).

Source: https://habr.com/ru/post/192912/



All Articles