Make Your Art UNIQUE - Build Your Own ASCII Art Generator in #dotnet
August 25, 2023
• 6,787 views
ASCII art... we've all seen it. But what if you wanted to make your own ASCII art? How could we build this in C#? Check out this video for a simple ASCII art generator that you can turn into your very own!
Want to read more about this? Check out this article:
https://www.devleader.ca/2023/08/25/generate-ascii-art-a-simple-how-to-in-c/
Full source code is available here:
https://github.com/ncosentino/DevLeader/tree/master/AsciiArtGenerator
Have you subscribed to my weekly newsletter yet? A 5-m...
View Transcript
all right so unless you're living under a rock you've probably seen asciard before and for those of you that haven't seen it well I'm sorry but I can explain it pretty quick for you traditionally images are represented by pixels but instead with ASCII art instead of using pixels for each of the individual parts of the image it's actually going to be an entire ASCII character and ASCII characters are the character encoding set from 0 to 255 so one single byte can represent one character so why am I making a video about ASCII art well recently I've been talking about projects that you can create as a beginner programmer that would help you learn how to program and I wanted to demonstrate with an asciar generator that something as simple or as silly as an ascare generator can actually teach you a lot about programming and
we can keep building and extending upon it so in this video I'm going to show you how to make your very own ask your generator and by the time we're done you'll have some ideas about how you can go extend this modify it and make it your own so that you can go learn new technologies new coding practices or just have your own spin on an ASCII art generator so with that said let's jump over to visual studio and start looking at some of the code alright so I'm here in visual studio and I just have a console application that actually is a functioning asciard generator but what I wanted to do was actually walk through some of the process for how I put this together and the reason that I want to show you this and explain it is because like I was saying
I think that there's a lot of learning opportunities when you start building things and even though I've been programming for over 20 years now there were still things that I got to learn and play with different concepts in this small little project that I thought were valuable so to start when we think about an asciar generator we're going to need some Source image so just to have this kind of coded right at the top here I have from line 3 to 11. it's a bit of a hack but what I have instead of just actually passing in arguments so that's what this args keyword is for I'm actually just directly assigning it because I was being lazy I could absolutely make it so that when I start debugging this I could pass in from Visual Studio the startup parameters but I was just playing around
with a bunch of different files like you can see from line three till six that's why there's some lines commented out so the file that I have referenced to start is just a happy face image that I got from the internet and this is just a quick check that if I wasn't hacking around like this that on the command line you would need to pass in One path to a file and we could do other checks like if the file exists or validating the file and all of that but we're not going to cover that right now the next part of making this code work is actually loading up that file so we just have a file stream here and beyond that what we're going to be doing is essentially calling into some of these classes that I'm going to walk through in just a
moment so once we have the stream loaded up for that image we're going to pass it into these other classes to do all of the work and then really we're done at that point so all of the magic happens in the other classes okay my first pass at working through this is actually using the system.drawing namespace now historically system.drawing is a Windows only package so this code that we're looking at right here isn't actually going to go running on Linux systems and visual studio actually tells us that when we're looking at the squigglies for under bitmap and things like that in visual Studios I hover over that it's actually telling me that you know this code may not work on all platforms so as I was writing this in my head I went oh crap I haven't really had to think about this because I
haven't been doing cross-platform stuff with system.drawing ever and the last time I used system.drawing was essentially when I was working in Windows forms applications so yes I might be dating myself a little bit there but that's the last time I had to use this but at least it's what I was familiar with to get started now the way that this class works is that we have a set of ASCII characters that we're going to be representing our pixels with so from left to right we have something that's more dense to something that's less dense so the way that this would work is the left side is darker representation on the right side is lighter all the way until we have a space which would be essentially like nothing is there we take the input stream that we created from the other part of the program
that we were just looking at pass it into a new bitmap which is from the system.drawing namespace and then this code here actually isn't perfect and has a lot to be desired I suppose but we need to be able to scale down the image accordingly so some things that we'd want to consider are how much real estate do we actually have to work with in the console right so if you had a 1920 by 1080 picture can we actually Jam that whole thing in the console without resizing it probably not very well so I just had some you know hacked in logic to scale it down by by a factor of 20 here and we could do other things and we probably should do other things with the aspect ratio so that's not being handled in this first pass of the code that I wrote
and then another thing that I wanted to work with and try out which ultimately I've ended up removing moving in the later variations of this but I figured we might as well set the console size when this is output because when you don't do it and the console is not big enough it actually just looks like a total mess when all the characters are written out so if we can size it accordingly then you can actually see the ASCII art in the way that it's intended to be represented this next part of the program from line 23 all the way to line 35 is essentially the bulk of the logic that this program has to use and the way that this is going to work is two Loops that we're going to have and you can see them here on line 24 and 26 so
these two lines right here and how this works is that we need to Loop across the height first and then the width because the inner loop is going to be running more frequently in this case right so we want to be going you know starting on row zero right so that's the height these rows and then on row 0 we need to print every column which is represented by the width so we're going to be going from top left all the way across the screen and then once we're done that row we would append a new line right so we'd go to the next row height would increment by one and then we would go print all the columns in the next row so that's the flow of the logic that we have with this Loop and that's just because if you had to go
down first and back up doing that in a console would be kind of ridiculous when there's a really easy approach to just being able to basically append characters right to the console so no fanciness if we go top left across and then down rows the body of this for Loop is actually where we're going to be grabbing the colors that we want to map to the ASCII characters and I just put some things on new lines there to make it a little bit more readable but the first part here we're actually getting the pixel color from the image based on the X and Y coordinates that we have and then from there we're going to be grayscaling it so we have some magic constants here you can play with these as you see fit but the idea ideas just that we're mapping the colored pixels
to a gray value and once we have that gray value what we can do is actually map that to an ASCII character that we've defined again up above here so a gray value is essentially going to be from the scale of you know if it were black essentially it would be this at symbol and if it were white it would be a space and the cool thing about how this is operating is that you can go add more ASCII characters so if you wanted to have more resolution in the different types of characters you were using you could go add elements to this array and the last part is that we're just going to be using a string Builder to append the characters as we're going and if you're thinking about this and optimizations and what future iterations of this could look like I mean
I did start with actually just a string that I was concatenating but if you've been playing around with strings and performance and memory usage in C sharp you'll know that just doing string plus equals another string especially across two loops and depending on how many characters you're dealing with it's just not really effective it's going to be making copies of those strings in memory as it's going so it's just really bad on performance in general so a string Builder is definitely a step up but perhaps we could be looking at things like character arrays and seeing if spans could help us here but I haven't gone to Benchmark any of this yet that is for a follow-up and if you're curious and you want to build your own you could actually try benchmarking this as well but really that's just the code that we have
and once we have this ASCII Builder 2 string we can go write that value right to the console and that should be our actual picture so let's go ahead and run this and see what the output looks like and like I said we have a happy face here so this is actually pretty awesome we have this face that is basically mapped to the Emoji just uh obviously scaled up pretty high and you can see based on the size of the font rate with this message at the bottom of the screen that this is actually a pretty big face it's taking up half my monitor that's 48 inches so it's a it's pretty pretty enormous on the screen it's kind of funny but yeah it's a very simple program that lets us output that but one tweak that I want to call out in the code
that's currently running versus what I was just showing you is actually the spacing of the characters so this is actually really bothering me throughout the creation of this ask air generator but something that's really important to note is that the actual width of the characters versus the height of the lines is not equal so if we were just to have one character across the screen right representing one pixel and then we want to go down representing a pixel in each new row they're actually not scaled the same way that you might expect so the height of a line is actually greater than the width of the characters so if you take a really close look especially if I zoom in here you'll notice that everything is actually doubled up and it's not coincidence it's not just that that's how the pixels worked out but I
actually had to go write two characters for every single character that we wanted to have printed out as a pixel and the result of that it was it would actually scale the width and the height to be more appropriate because otherwise every picture that I would generate was just super skinny so about half the size and I thought I had some aspect scaling issues with how my pixels were being pulled out but then I realized I wasn't even aspect scaling to begin with so I couldn't have messed it up it was always going to be exactly the right aspect ratio so truly it just had to do with the output into the console and that might be something you want to keep in mind because if your output that you're going to be using is not exactly just a console maybe you want to represent
this some other way maybe you don't want to double up the characters so you do have to think about the actual width of the characters versus the height of them as well all right now I'm going to jump over to a very similar class and this is just the second iteration of the ASCII art generator that I created and this one actually does not use system.drawing because I wanted to make sure that what I was going to release would be available on Linux and windows as well so using this Library we do get cross-platform usage and again the algorithm is going to look extremely similar right so we have the ask characters what we do need to do here though is actually load up an image and then based on the format of that image we're just going to clone it so that we have
it as this rgba 32 and that format the actual type changes for image here I know I'm using vars so it might not be obvious but we get an image with a type parameter of rgba 32 and the reason that we need that it's subtle but if I scroll down a little bit further again this algorithm is almost the exact same that you saw before but when we pull out the pixel which is right here we can't do this on a normal image type that comes from image sharp which is the library I'm using but if it's an rgba 32 image then we get this double indexer to be able to pull out the pixel color so again doing that clone that we saw at the top of this actually allows us to pull out the pixel color otherwise this part of the algorithm is
the exact same so I won't touch on it because we just did and the part at the bottom that looks a little bit different is that I started to change the API of this a little bit so again reflecting on why this might be an interesting project for newer folks is I got to do one iteration of it and then once I had it working I said okay well I can clean up the API a little bit more to something that I would like which is give me a string and then also give me the dimensions that we're expecting for this ASCII art and sometimes you don't know that you want these other types of information coming out of your API until you start working with it so for me it truly was an iterative approach to be able to see if I could improve
this the other part that changed if you recall I was talking about aspect ratio a little bit I did introduce this aspect ratio kind of scaling here but one thing that's not actually called out is that I'm not taking into account how far down we actually need to scale this so again I have a hard-coded by 20 here for the width and then I'm just kind of scaling the height to be by the aspect ratio that was the original one from the image so is 20 the right number here I don't know for every image probably not for the ones I was playing with it did seem to work just so I could fit it on my screen but there are some optimizations we could absolutely do like figuring out what an optimal output size is and then having the user input that so that
they have their own preference there and then we can scale the image accordingly to match that output size so again that could totally be an extension that you want to go build for this instead of just giving it an image path you can say what's the source image and then how wide or how tall do you want this image to be when it's output okay now that I've shown you both implementations of a system.drawing ASCII art generator and an image sharp asciard generator I wanted to show you how I ended up refactoring this even further for no good reason except just to kind of play around with the code and understand it a little bit better so what I decided to do was well in both my implementations I kind of showed you that this algorithm existed and it was almost identical right I ended
up improving the aspect ratio part and I ended up having this part at the end that changed but otherwise this whole Loop that was in the middle was identical the only difference was that how I was getting the pixel between the types were system.drawing and image sharp looked different so what I decided to do was actually pull this algorithm out into its own class which I just called a generator in this case and then I actually have an interface for an image source and what's really cool about this is depending on which implementation I want to use system.drawing or image sharp I actually have a different implementation for I image source so if I press Ctrl F12 in Visual Studio it will pull up this part at the bottom that lets me go pick between which classes so I have a GDI image source and
this is going to be for system.drawing so you can see that it has a width the height aspect ratio and then this get pixel part and all of this is really just kind of calling into the base stuff that we already had available so it's just a really thin wrap around it and the same thing if I go to the image sharp one where we have this image rgba 32 like I was talking about and then we have the width the height and the aspect ratio but getting pixel works because we have that image rgba 32 so I have two implementations of this interface and that means that when we want to go call the code inside of this generator we just pass in any one of them and now this code is totally agnostic to which type of image source you'd like to use
all right so to summarize we had a really quick implementation of an ASCII art generator in C sharp as you can see it's very simple it's really only coming down to two Loops that we go from the height so we get all the rows and then the inner loop is going to be the column so going across the width and as we pull out each individual pixel we're mapping that pixels color to grayscale and then picking a character that represents a different amount of density right so the at symbol wasn't the most dense to a space which is clearly empty and then mapping that grayscale value to that character and then once we have that we format a full string with that and put it right to the console so the other things that I touched on in this video were just that I had
two completely different implementations using the system.drawing namespace and then image sharp as a Linux supported library that we could also use and then from there I walked you through how I refactored my code to actually be able to have the core algorithm and then sort of pass in with an interface the different implementations that I just mentioned so still lots of things to be desired and that's the case with a lot of these really simple projects when we're playing around with them so for sure we could do things like aspect scaling we could have an output size selection that the user could input some parameters and something else I wanted to call out is that yeah this is just a console application that I have running on my computer but what's stopping from taking this type of code and making a web service for it
right what if you went and made an asp.net core application where someone could upload a photo to your server and then server side you go create this ASCII art and pass it back to the user you could have a whole UI for it where they could put in the you know the information for the output size and all of that you could have toggles to make it so that maybe it's formatted for like a console like I was showing you earlier with the different width of the characters versus the height so you could have options for that maybe you could make it colorized so it could output to something like HTML and you could actually drop in ASCII art as HTML that's all colored and stuff like there's so many cool options that you could do here and extend this and I think that when
we look at projects like this especially from the perspective of learning you're only limited by your creativity so if you're thinking about the different things that you want to go learn the different Technologies if you start with a project like this you can start to think about how you might want to go learn those Technologies and apply them to the project so I've already mentioned things like a web service so you have asp.net that could be used here you get front end and back-end development opportunities Cloud hosting what if you want to store some of this stuff in a database for caching and things like that so many options and I think that that's why working on projects like this can be super exciting even if they're not set up to try and generate you a ton of money or anything like that I hope
you enjoyed this I hope you try out making your own ascr generator and let me know in the comments if you try and what you end up coming up with so thanks so much for watching and we'll see you later
Frequently Asked Questions
What is ASCII art and how does it differ from traditional images?
ASCII art is a form of visual art that uses ASCII characters instead of pixels to represent images. Unlike traditional images that are made up of pixels, ASCII art uses characters from the ASCII character set to create representations of images, allowing for a unique and stylized output.
What programming languages or technologies do I need to create my own ASCII art generator?
In this video, I used C# and the .NET framework to build the ASCII art generator. Specifically, I demonstrated how to use Visual Studio for development and explored libraries like System.Drawing and ImageSharp for image processing.
Can I extend the ASCII art generator to include more features?
Absolutely! I encourage you to modify and extend the ASCII art generator. You can add features like user-defined output sizes, colorized ASCII art, or even create a web service where users can upload images and receive ASCII art in return. The possibilities are endless!
These FAQs were generated by AI from the video transcript.