#743 – ASCII Art Generator
December 25, 2012 Leave a comment
Here’s a little ASCII Art generator that I wrote in C#. Merry Christmas!
Main program, which accepts full path to image file to be converted:
class Program { /// <summary> /// Usage: /// Arg 1 - Full path to bitmap file (e.g. JPG, PNG) /// Arg 2 - Width of target image, in # characters (e.g. 120) /// Output: File with same name as input file but .txt extension /// </summary> /// <param name="args"></param> static void Main(string[] args) { try { if (args.Length < 2) { Console.WriteLine("Usage:"); Console.WriteLine(" AscArt filename output-width [diagLog]"); } else { string inputFile = args[0]; int outputWidth = int.Parse(args[1]); FileInfo fi = new FileInfo(inputFile); if (!fi.Exists) throw new Exception(string.Format("File {0} not found", inputFile)); string outputFile = Path.Combine(fi.DirectoryName, Path.GetFileNameWithoutExtension(inputFile) + ".txt"); Bitmap bmInput = new Bitmap(inputFile); if (outputWidth > bmInput.Width) throw new Exception("Output width must be <= pixel width of image"); // Generate the ASCII art AscArt.GenerateAsciiArt(bmInput, outputFile, outputWidth); } } catch (Exception xx) { Console.WriteLine(string.Format("Fatal exception: {0}", xx)); } } }
Here’s the source code for the AscArt class:
public static class AscArt { // Typical width/height for ASCII characters private const double FontAspectRatio = 0.6; // Available character set, ordered by decreasing intensity (brightness) private const string OutputCharSet = "@%#*+=-:. "; // Alternate char set uses more chars, but looks less realistic private const string OutputCharSetAlternate = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. "; public static void GenerateAsciiArt(Bitmap bmInput, string outputFile, int outputWidth) { // pixelChunkWidth/pixelChunkHeight - size of a chunk of pixels that will // map to 1 character. These are doubles to avoid progressive rounding // error. double pixelChunkWidth = (double)bmInput.Width / (double)outputWidth; double pixelChunkHeight = pixelChunkWidth / FontAspectRatio; // Calculate output height to capture entire image int outputHeight = (int)Math.Round((double)bmInput.Height / pixelChunkHeight); // Generate output image, row by row double pixelOffSetTop = 0.0; StringBuilder sbOutput = new StringBuilder(); for (int row = 1; row <= outputHeight; row++) { double pixelOffSetLeft = 0.0; for (int col = 1; col <= outputWidth; col++) { // Calculate brightness for this set of pixels by averaging // brightness across all pixels in this pixel chunk double brightSum = 0.0; int pixelCount = 0; for (int pixelLeftInd = 0; pixelLeftInd < (int)pixelChunkWidth; pixelLeftInd++) for (int pixelTopInd = 0; pixelTopInd < (int)pixelChunkHeight; pixelTopInd++) { // Each call to GetBrightness returns value between 0.0 and 1.0 int x = (int)pixelOffSetLeft + pixelLeftInd; int y = (int)pixelOffSetTop + pixelTopInd; if ((x < bmInput.Width) && (y < bmInput.Height)) { brightSum += bmInput.GetPixel(x, y).GetBrightness(); pixelCount++; } } // Average brightness for this entire pixel chunk, between 0.0 and 1.0 double pixelChunkBrightness = brightSum / pixelCount; // Target character is just relative position in ordered set of output characters int outputIndex = (int)Math.Floor(pixelChunkBrightness * OutputCharSet.Length); if (outputIndex == OutputCharSet.Length) outputIndex--; char targetChar = OutputCharSet[outputIndex]; sbOutput.Append(targetChar); pixelOffSetLeft += pixelChunkWidth; } sbOutput.AppendLine(); pixelOffSetTop += pixelChunkHeight; } // Dump output string to file File.WriteAllText(outputFile, sbOutput.ToString()); } }
So converting this image:
Gives us the following:
Enjoy!