ASCII Art is made using nothing but the letters found on the known that come from the American Standard Code for Information Interchange (ASCII). You can see an example below, found at www.asciiArt.eu.
$$$$$$$$ $$$$$$ m$$$$$$$$$ m$"""" "$$m $$$$$$$$$$ $" m$$$$m""$m mm$$$$$$$$$$$$$$$"" $ "$$mm$$ m"" """$$$$$mm "$$$$$ mmm$$m$$m$$mmmmm """"$$$" m$$$" ""$m $$$""""""$$$mm $$$$m $ "" mm$$$$ mm" mm$$$$$$$$$$$ $ m$$$$"" ""$$$ $ $$"" m "$$m $ """ mm$$$$$$$$$$ $ m$$$$$""""$$$" $ mm mm $$" "$$ $ $"$$ $"$$ "$"""" $"m" "m$" m"""" m$ m"""m "mmm"" $ mmmm "" mmm$$""""""$$ m""""$ m$$" mm$$$$$$$ mm $$ $$$$$$$$$$$$ mmm "$m "$m "$$$$$$$$$$$$$$$ m$m" m " ""$$$$$$$$$$$$" m$$$$ ""mm $""$$$$$"" m$$$$ m mmm " "m m$$$$$$ mm$mmmm "" ""mmm mmm$$""$$$" $$""m " """"" "$mm$" m $$ m m$" $$ "$$$$$mm$"" "" """"
This Lab is based on one of Robert Heaton's Advanced Beginners Projects. I have made starter code for Java Students about to take the AP Computer Science A Exam. You need to know somehting about 2D Arrays and String Objects.
Choose the first image that you want to convert into ASCII art. It’s good to start with an image around 640×480 or smaller (Of course, you can shrink and crop an image of any size down to this). It can be in color, a photo, or a drawing. I think it is easier to get nice results from a cartoon or logo or a high contrast photo or drawing. More subtle photos look good in a very large array of characters, so you would need to miniaturize or print on a large format printer to get a good effect.
Light pixels will be represented by small characters like a dot (.), which leave lots of the background exposed. On the other extreme, dense characters like $ are used to cover up the background. You can easily invert this later.
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import java.awt.*; /** * PickPicture is Step 0 of the ASCII Art Lab * @author Chris Thiel, OFMCap * @version 25 Mar 2023 */ public class PickPicture extends JPanel { private String fileName; private BufferedImage source; private Font titleFont, regularFont; public PickPicture(String fileName) { titleFont = new Font("Roman", Font.BOLD, 32); regularFont = new Font("Helvetica", Font.PLAIN, 24); try { // the line that reads the image file this.fileName = fileName; this.source = ImageIO.read(new File(fileName)); System.out.println(fileName + " read "); System.out.println(source.getWidth() + " by " + source.getHeight()); } catch (IOException e) { System.out.println("Whoa... something's not right:\n"+ "Make sure the file is \n 1. In the project folder,\n " + "2. The file name is perfect-including capital letters\n " + "3. The extension is right-like .png or .jpg\n " + e); } } public int getWidth() {return source.getWidth();} public int getHeight() {return source.getHeight();} public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(),getHeight()); g.drawImage(source, 0, 0, null); g.setColor(Color.BLACK);// Use WHITE if the image is dark g.setFont(titleFont); g.drawString(fileName, 20, 40); g.setFont(regularFont); g.drawString ( getWidth() + " by " + getHeight(), 20, 60); } public static void main(String[] args) { PickPicture pic= new PickPicture("myPicture.jpg"); // change this to the picute you picked JFrame window = new JFrame("Pick Picture"); window.setSize(pic.getWidth(), pic.getHeight()); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.getContentPane().add(pic); window.setVisible(true); } }
import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class Step1 { private BufferedImage image; private String fileName; public Step1(String fileName) { try { this.fileName = fileName; image = ImageIO.read(new File(fileName)); System.out.println(fileName + " read "); System.out.println("Demensions of " + fileName + ": "); // Print the width and height of the image using a BufferedImage method. } catch (IOException e) { System.out.println("Whoa... something's not right:\n"+e); } } public static void main(String[] args) { new Step1("My_Picture.jpg"); } }
import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; /** * Start Step2 with this code. * The image will return a single line of ints that represent color * and you need to make a 2D Array of Color from it. * * @author Chris Thiel, OFMCap * @version 25 Mar 2023 */ public class Step2 { private BufferedImage image; private String fileName; private int width,height; private int[] rgb; // for 8bit RGB packed in an int array private Color[][] px; public Step2(String fileName) { try { // the line that reads the image file this.fileName = fileName; image = ImageIO.read(new File(fileName)); System.out.println(fileName + " read "); width = image.getWidth(); height = image.getHeight(); System.out.println("Demensions of " + fileName + ": " + width + " by " + height); rgb = image.getRGB(0,0, width, height, null, 0, width); System.out.println(rgb.length); // rgb is a 1D array, with the rows listed one after the other. ///to convert to a 2D array use something like this: // grid (x,y) = rgb[ y*width + x]; // Make a new 2D array of color name px, and fill it from the 1D rgb array // for example, px[0][0] = new Color ( rgb[0] ); } catch (IOException e) { System.out.println("Whoa... something's not right:\n"+e); } } public Color[][] getPx() { return px;} public static void main(String[] args) { new Step2("My_Picture.jpg"); } }
You can see what your new 2D Array of Color looks like with Step2Tester.java
Now that you have a 2D Array of Color
, you can use the Color
methods of getRed()
, getGreen()
, and getBlue()
to assign a single brightness number. There are many different ways to map RGB values to brightness, and each produces a slightly different style of transformed image. Think of them like Instagram filters. Some examples:
(R + G + B) / 3
max(R, G, B) + min(R, G, B) / 2
0.2126 R + 0.7152 G + 0.0722 B
sqrt( 0.299 R^2 + 0.587 G^2 + 0.114 B^2)
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.*; /** * This is Step3 of the ASCII Art Lab: * Convert the 2D Color Array into a * 2D int array of brightness * * You need a functional Step2 class * in this project folder for this to work. * this * @author Chris Thiel, OFMCap * @version 25 Mar 2023 */ public class Step3 extends JPanel { private String fileName; private Font titleFont, regularFont; private Color[][] clr; private int[][] brightness; public Step3(String fileName) { this.fileName = fileName; this.clr = new Step2(fileName).getPx(); titleFont = new Font("Roman", Font.BOLD, 32); regularFont = new Font("Helvetica", Font.PLAIN, 24); // Make a 2D int array that same number of rows and columns // as the Color array //Fill the brightness array with yout choice of conversion method // (like Average, Lightness, Luminocity, or your own) } public int average(Color c) { // your code here } public int luminance(Color c) { // your code here } public int lightness(Color c) { //your code here } public int luminosity(Color c){ // your code here } public int getWidth() { return brightness.length;// width is max x i.e, the max col } public int getHeight() { return brightness[0].length; // height is the max y, i.ie max row } public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(Color.WHITE); g.fillRect(0, 0, getWidth(),getHeight()); //g.drawImage(source, 0, 0, null); for (int y=0; y < getHeight(); y++) for(int x=0; x < getWidth(); x++) { int b = brightness[x][y]; g.setColor( new Color(b,b,b) ); g.fillRect( x, y, 1, 1); } g.setColor(Color.BLACK);// Use WHITE if the image is dark g.setFont(titleFont); g.drawString(fileName, 20, 40); g.setFont(regularFont); g.drawString ( getWidth() + " by " + getHeight() + "- Drawn from 2DColor Array", 20, 70); } public int[][] getBrightness() { return brightness; } public static void main(String[] args) { Step3 pic= new Step3("MyPicture.jpg"); // change this to the picture you picked JFrame window = new JFrame("Step 3"); window.setSize(pic.getWidth(), pic.getHeight()); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.getContentPane().add(pic); window.setVisible(true); } }
You can experiment with different ways to map brightnesses to characters, but a good place to start is the string in the starter code. The characters in it are ordered from thinnest to boldest, which means lightest to darkest. To get started you may wish to use this arrangement
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.*; import java.nio.file.Files; import java.io.IOException; import java.nio.file.Paths; /** * This is Step4 of the ASCII Art Lab: * Convert your lightness values into * ASCII Characters. * * This code requires a functional Step3 * * @author Chris Thiel, OFMCap * @version 25 Mar 2023 */ public class Step4 { private String fileName; private Font titleFont, regularFont; private int[][] brightness; private char[][] ascii; public Step4(String fileName) { this.fileName = fileName; this.brightness = new Step3(fileName).getBrightness(); titleFont = new Font("Roman", Font.BOLD, 32); regularFont = new Font("Helvetica", Font.PLAIN, 24); //Make a 2D int array that matched the dimentions of the Brightness array ascii = new char[brightness.length][brightness[0].length]; //Fill the asciii array with your choice of conversion method // your code here } /** * asciiChar will take your lightValue and * convert it to a proportion of 255. * (To invert the dark and light, the the proportion is 1.0 - proportion.) * * The proportion is used to select the charactor from the character spectrum * */ public char asciiChar( int lightValue) { String chars = ""; // list the chars from one extreame to the other int max = chars.length(); // your code here } public int getWidth() { return brightness.length;// width is max x i.e, the max col } public int getHeight() { return brightness[0].length; // height is the max y, i.ie max row } /** * The ascii array is constructed with a newline "\n" between the rows */ public String toString(){ // your code here } public void writeToFile() { String name = fileName.substring(0, fileName.indexOf(".")) + ".txt"; try { Files.write(Paths.get(name), toString().getBytes()); } catch (IOException e){ e.printStackTrace(); } } public static void main(String[] args) { System.out.println("Converting Pricture to ASCII Art"); Step4 pic= new Step4("MyPicture.jpg"); // change this to the picture you want System.out.println(pic); pic.writeToFile(); } }
Characters tend to be three times taller than wide, so you could replace each character with three. If that is too large, you could make one character represent the average of three columns.