Saving PNG to GIF

Image IO read and Image IO write are focus of this code. A key portion of working with Images, or any file, is to know location of the input and output directories.

import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;

public class ImageIOTest {    

    public static void main( String[] args ){
       BufferedImage img = null;  // buffer type 
        try {
            // Name of file and directories
            String name = "MonaLisa";
            String in = "images/";
            String out = "images/tmp/";

            // Either use URL or File for reading image using ImageIO
            File imageFile = new File(in + name + ".png");
            img = ImageIO.read(imageFile);  // set buffer of image data

            // ImageIO Image write to gif in Java
            // Documentation https://docs.oracle.com/javase/tutorial/2d/images/index.html
            ImageIO.write(img, "gif", new File(out + name + ".gif") );  // write buffer to gif

        } catch (IOException e) {
              e.printStackTrace();
        }
        System.out.println("Success");
    }
}
ImageIOTest.main(null);
Success

Image Scaling and ASCII Conversion

In this example we print out a row of text for each row in the image. However, it seems as if the image is too tall. To address this problem, try to output a single character per block of pixels. In particular, average the grayscale values in a rectangular block that’s twice as tall as it is wide, and print out a single character for this block.

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.awt.Graphics2D;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;

public class Pics {
    private final String inDir = "images/"; // location of images
    private final String outDir = "images/tmp/";  // location of created files
    private String inFile;
    private String resizedFile;
    private String asciiFile;
    private String ext;   // extension of file
    private long bytes;
    private int width;
    private int height;

    // Constructor obtains attributes of picture
    public Pics(String name, String ext) {
        this.ext = ext;
        this.inFile = this.inDir + name + "." + ext;
        this.resizedFile = this.outDir + name + "." + ext;
        this.asciiFile = this.outDir + name + ".txt";
        this.setStats();
    }

    
    // An image contains metadata, namely size, width, and height
    public void setStats() {
        BufferedImage img;
        try {
            Path path = Paths.get(this.inFile);
            this.bytes = Files.size(path);
            img = ImageIO.read(new File(this.inFile));
            this.width = img.getWidth();
            this.height = img.getHeight();
        } catch (IOException e) {
        }
    }

    // Console print of data
    public void printStats(String msg) {
        System.out.println(msg + ": " + this.bytes + " " + this.width + "x" + this.height + "  " + this.inFile);
    }

    // Convert scaled image into buffered image
    public static BufferedImage convertToBufferedImage(Image img) {

        // Create a buffered image with transparency
        BufferedImage bi = new BufferedImage(
                img.getWidth(null), img.getHeight(null),
                BufferedImage.TYPE_INT_ARGB);

        // magic?
        Graphics2D graphics2D = bi.createGraphics();
        graphics2D.drawImage(img, 0, 0, null);
        graphics2D.dispose();

        return bi;
    }
    
    // Scale or reduce to "scale" percentage provided
    public void resize(int scale) {
        BufferedImage img = null;
        Image resizedImg = null;  

        int width = (int) (this.width * (scale/100.0) + 0.5);
        int height = (int) (this.height * (scale/100.0) + 0.5);

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            // create a new BufferedImage for drawing
            resizedImg = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        } catch (IOException e) {
            return;
        }

        try {
            ImageIO.write(convertToBufferedImage(resizedImg), this.ext, new File(resizedFile));
        } catch (IOException e) {
            return;
        }
        
        this.inFile = this.resizedFile;  // use scaled file vs original file in Class
        this.setStats();
    }
    
    // convert every pixel to an ascii character (ratio does not seem correct)
    public void convertToAscii() {
        BufferedImage img = null;
        PrintWriter asciiPrt = null;
        FileWriter asciiWrt = null;

        try {
            File file = new File(this.asciiFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        try {
            asciiPrt = new PrintWriter(asciiWrt = new FileWriter(this.asciiFile, true));
        } catch (IOException e) {
            System.out.println("ASCII out file create error: " + e);
        }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
        }

        for (int i = 0; i < img.getHeight(); i++) {
            for (int j = 0; j < img.getWidth(); j++) {
                Color col = new Color(img.getRGB(j, i));
                double pixVal = (((col.getRed() * 0.30) + (col.getBlue() * 0.59) + (col
                        .getGreen() * 0.11)));
                try {
                    asciiPrt.print(asciiChar(pixVal));
                    asciiPrt.flush();
                    asciiWrt.flush();
                } catch (Exception ex) {
                }
            }
            try {
                asciiPrt.println("");
                asciiPrt.flush();
                asciiWrt.flush();
            } catch (Exception ex) {
            }
        }
    }

    // conversion table, there may be better out there ie https://www.billmongan.com/Ursinus-CS173-Fall2020/Labs/ASCIIArt
    public String asciiChar(double g) {
        String str = " ";
        if (g >= 240) {
            str = " ";
        } else if (g >= 210) {
            str = ".";
        } else if (g >= 190) {
            str = "*";
        } else if (g >= 170) {
            str = "+";
        } else if (g >= 120) {
            str = "^";
        } else if (g >= 110) {
            str = "&";
        } else if (g >= 80) {
            str = "8";
        } else if (g >= 60) {
            str = "#";
        } else {
            str = "@";
        }
        return str;
    }

    // tester/driver
    public static void main(String[] args) throws IOException {
        Pics monaLisa = new Pics("MonaLisa", "png");
        monaLisa.printStats("Original");
        monaLisa.resize(33);
        monaLisa.printStats("Scaled");
        monaLisa.convertToAscii();

        Pics pumpkin = new Pics("pumpkin", "png");
        pumpkin.printStats("Original");
        pumpkin.resize(33);
        pumpkin.printStats("Scaled");
        pumpkin.convertToAscii();

        Pics Autumn = new Pics("Autumn", "png");
        Autumn.printStats("Original");
        Autumn.resize(33);
        Autumn.printStats("Scaled");
        Autumn.convertToAscii();
    }
}
Pics.main(null);
Original: 499298 389x413  images/MonaLisa.png
Scaled: 55625 128x136  images/tmp/MonaLisa.png
Original: 39392 302x265  images/pumpkin.png
Scaled: 10497 100x87  images/tmp/pumpkin.png
Original: 1071473 1522x954  images/Autumn.png
Scaled: 440949 502x315  images/tmp/Autumn.png

Setup for Color Scales

Prior to writing the code for color scales, it is important to create a method that will acquire data for a given image. This helps prevent the inefficient rewriting of code.

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.awt.Graphics2D;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;

abstract class ImageBlueprint {
    protected final String inDir = "images/"; // location of images
    protected final String outDir = "images/tmp/";  // location of created files
    protected String inFile;
    protected String resizedFile;
    protected String colorFile;
    protected String ext;   // extension of file
    protected long bytes;
    protected int width;
    protected int height;

    // get attributes of picture
    public ImageBlueprint(String name, String ext) {
        this.ext = ext;
        this.inFile = this.inDir + name + "." + ext;
        this.resizedFile = this.outDir + name + "." + ext;
        this.colorFile = this.outDir + name + "New" + ".png";
        this.setStats();
    }

    public ImageBlueprint(String image) {
        this(image, "png");
    }
   
    // An image contains metadata, namely size, width, and height
    public void setStats() {
        BufferedImage img;
        try {
            Path path = Paths.get(this.inFile);
            this.bytes = Files.size(path);
            img = ImageIO.read(new File(this.inFile));
            this.width = img.getWidth();
            this.height = img.getHeight();
        } catch (IOException e) {
        }
    }

    // Scale or reduce to "scale" percentage provided
    public void resize(int scale) {
        BufferedImage img = null;
        Image resizedImg = null;  

        int width = (int) (this.width * (scale/100.0) + 0.5);
        int height = (int) (this.height * (scale/100.0) + 0.5);

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            // create a new BufferedImage for drawing
            resizedImg = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        } catch (IOException e) {
            return;
        }


        //ImageIO.write(convertToBufferedImage(resizedImg), this.ext, new File(resizedFile));

        
        this.inFile = this.resizedFile;  // use scaled file vs original file in Class
        this.setStats();
    }
    
    // Will be used later
    protected abstract void conversion(); 
}

Original Autumn

Color Scaling

For the primary colors (RGB), all that's needed is to set the values of the non scaled color to be 00.

Gray-scaling is more complicated, and requires averaging the R, G, and B values, meaning add the values together and divide by three.

public class RedConversion extends ImageBlueprint {

    
    public RedConversion(String name, String ext) {
        super(name, ext);
        this.colorFile = this.outDir + name + "Red" + ".png";
    }

    public RedConversion(String name) {
        super(name);
        this.colorFile = this.outDir + name + "Red" + ".png";
    }

    @Override
    protected void conversion() {
        BufferedImage img = null;
        PrintWriter colorPrt = null;
        FileWriter colorWrt = null;
        
        
        // Just deletes file if already exists in tmp
        try {
            File file = new File(this.colorFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        // try {
        //     colorPrt = new PrintWriter(colorWrt = new FileWriter(this.colorFile, true));
        // } catch (IOException e) {
        //     // TODO Auto-generated catch block
        //     e.printStackTrace();
        // }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        for (int i = 0; i < this.height; i++) {
            for (int j = 0; j < this.width; j++) {
                Color col = new Color(img.getRGB(j, i));
                int rgb = new Color(col.getRed(), 0, 0).getRGB();
                img.setRGB(j, i, rgb);

            }
        }

        try {
            ImageIO.write(img, "png", new File(this.colorFile) );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        
    }

    public static void main(String[] args) {
        RedConversion autumn = new RedConversion("autumn", "png");
        autumn.conversion();
    }

}
RedConversion.main(null);

Red-Scaled Autumn

public class GreenConversion extends ImageBlueprint {
    public GreenConversion(String name, String ext) {
        super(name, ext);
        this.colorFile = this.outDir + name + "Green" + ".png";
    }

    public GreenConversion(String name) {
        super(name);
        this.colorFile = this.outDir + name + "Green" + ".png";
    }

    @Override
    protected void conversion() {
        BufferedImage img = null;
        PrintWriter colorPrt = null;
        FileWriter colorWrt = null;
        
        
        // Just deletes file if already exists in tmp
        try {
            File file = new File(this.colorFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        // try {
        //     colorPrt = new PrintWriter(colorWrt = new FileWriter(this.colorFile, true));
        // } catch (IOException e) {
        //     // TODO Auto-generated catch block
        //     e.printStackTrace();
        // }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        for (int i = 0; i < this.height; i++) {
            for (int j = 0; j < this.width; j++) {
                Color col = new Color(img.getRGB(j, i));
                int rgb = new Color(0, col.getGreen(), 0).getRGB();
                img.setRGB(j, i, rgb);

            }
        }
    
        try {
            ImageIO.write(img, "png", new File(this.colorFile) );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        
    }

    public static void main(String[] args) {
        GreenConversion Autumn = new GreenConversion("Autumn", "png");
        Autumn.conversion();
    }

}
GreenConversion.main(null);

Green-Scaled Autumn

public class BlueConversion extends ImageBlueprint {
    public BlueConversion(String name, String ext) {
        super(name, ext);
        this.colorFile = this.outDir + name + "Blue" + ".png";
    }

    public BlueConversion(String name) {
        super(name);
        this.colorFile = this.outDir + name + "Blue" + ".png";
    }

    @Override
    protected void conversion() {
        BufferedImage img = null;
        PrintWriter colorPrt = null;
        FileWriter colorWrt = null;
        
        
        // Just deletes file if already exists in tmp
        try {
            File file = new File(this.colorFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        // try {
        //     colorPrt = new PrintWriter(colorWrt = new FileWriter(this.colorFile, true));
        // } catch (IOException e) {
        //     // TODO Auto-generated catch block
        //     e.printStackTrace();
        // }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        for (int i = 0; i < this.height; i++) {
            for (int j = 0; j < this.width; j++) {
                Color col = new Color(img.getRGB(j, i));
                int rgb = new Color(0, 0, col.getBlue()).getRGB();
                img.setRGB(j, i, rgb);

            }
        }

        try {
            ImageIO.write(img, "png", new File(this.colorFile) );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        
    }

    public static void main(String[] args) {
        BlueConversion Autumn = new BlueConversion("Autumn", "png");
        Autumn.conversion();
    }

}
BlueConversion.main(null);

Blue-Scaled Autumn

public class GrayConversion extends ImageBlueprint {
    public GrayConversion(String name, String ext) {
        super(name, ext);
        this.colorFile = this.outDir + name + "Gray" + ".png";
    }

    public GrayConversion(String name) {
        super(name);
        this.colorFile = this.outDir + name + "Gray" + ".png";
    }

    @Override
    protected void conversion() {
        BufferedImage img = null;
        PrintWriter colorPrt = null;
        FileWriter colorWrt = null;
        
        
        // Just deletes file if already exists in tmp
        try {
            File file = new File(this.colorFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        // try {
        //     colorPrt = new PrintWriter(colorWrt = new FileWriter(this.colorFile, true));
        // } catch (IOException e) {
        //     // TODO Auto-generated catch block
        //     e.printStackTrace();
        // }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        for (int i = 0; i < this.height; i++){
            for (int j = 0; j < this.width; j++){
                Color col = new Color(img.getRGB(j,i));
                int rgb = new Color(((col.getRed()+ col.getGreen()+ col.getBlue())/3), ((col.getRed()+ col.getGreen()+ col.getBlue())/3), ((col.getRed()+ col.getGreen()+ col.getBlue())/3)).getRGB();
                img.setRGB(j, i, rgb);
            }
        }

        try {
            ImageIO.write(img, "png", new File(this.colorFile) );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        
    }

    public static void main(String[] args) {
        GrayConversion Autumn = new GrayConversion("Autumn", "png");
        Autumn.conversion();
    }

}
GrayConversion.main(null);

Gray-Scaled Autumn

ASCII

We are going to convert images to ASCII characters. The existing problem with the current method is that it stretched the image out, hurting the appeal. The best solution is to take 4 rows and 2 columns of pixels and convert them into a single ASCII character.

The finished result still looks horrible.

public class ASCIIConversion extends ImageBlueprint {
    
    public ASCIIConversion(String name, String ext) {
        super(name, ext);
        this.colorFile = this.outDir + name + "ASCII" + ".txt";
    }

    public ASCIIConversion(String name) {
        super(name);
        this.colorFile = this.outDir + name + "ASCII" + ".txt";
    }

    @Override
    protected void conversion() {
        
        // Controls how big chunks are taken for ASCII characters
        final int XLENGTH = 1;
        final int YLENGTH = 2;

        BufferedImage img = null;
        PrintWriter asciiPrt = null;
        FileWriter asciiWrt = null;
        Color col = null;

        try {
            File file = new File(this.colorFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        try {
            asciiPrt = new PrintWriter(asciiWrt = new FileWriter(this.colorFile, true));
        } catch (IOException e) {
            System.out.println("ASCII out file create error: " + e);
        }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
        }

        for (int i = 0; i < img.getHeight(); i += 4) {
            for (int j = 0; j < img.getWidth(); j += 2) {

                // colorSum stores total sum of RGB values, counter keeps track of how many are being counted
                // colorSum/counter = average
                // average is taken to get ASCII character
                double colorSum = 0;
                int counter = 0;

                for (int k = 0; k < XLENGTH; k++) {
                    for (int l = 0; l < YLENGTH; l++) {
                        if (k < img.getWidth() && l < img.getHeight()) {
                            counter++;
                            col = new Color(img.getRGB(j + k, i + l));
                            colorSum += (((col.getRed() * 0.30) + (col.getBlue() * 0.59) + (col
                                .getGreen() * 0.11)));
                        }
                    }
                }

                double average = colorSum / counter;
                try {
                    asciiPrt.print(asciiChar(average));
                    asciiPrt.flush();
                    asciiWrt.flush();
                } catch (Exception ex) {
                }

            }
            try {
                asciiPrt.println("");
                asciiPrt.flush();
                asciiWrt.flush();
            } catch (Exception ex) {
            }
        }
    }

    public String asciiChar(double g) {
        String str = " ";

        if (g >= 240) {
            str = " ";
        } else if (g >= 210) {
            str = ".";
        } else if (g >= 190) {
            str = "*";
        } else if (g >= 170) {
            str = "+";
        } else if (g >= 120) {
            str = "^";
        } else if (g >= 110) {
            str = "&";
        } else if (g >= 80) {
            str = "8";
        } else if (g >= 60) {
            str = "#";
        } else {
            str = "@";
        }

        return str;
    }
    
    public static void main(String[] args) {
        ASCIIConversion Autumn = new ASCIIConversion("Autumn", "png");
        Autumn.conversion();
    }

}
ASCIIConversion.main(null);
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.awt.Graphics2D;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;

public class Pics {
    private final String inDir = "images/"; // location of images
    private final String outDir = "images/tmp/";  // location of created files
    private String inFile;
    private String resizedFile;
    private String asciiFile;
    private String ext;   // extension of file
    private long bytes;
    private int width;
    private int height;

    // Constructor obtains attributes of picture
    public Pics(String name, String ext) {
        this.ext = ext;
        this.inFile = this.inDir + name + "." + ext;
        this.resizedFile = this.outDir + name + "." + ext;
        this.asciiFile = this.outDir + name + ".txt";
        this.setStats();
    }

    
    // An image contains metadata, namely size, width, and height
    public void setStats() {
        BufferedImage img;
        try {
            Path path = Paths.get(this.inFile);
            this.bytes = Files.size(path);
            img = ImageIO.read(new File(this.inFile));
            this.width = img.getWidth();
            this.height = img.getHeight();
        } catch (IOException e) {
        }
    }

    // Console print of data
    public void printStats(String msg) {
        System.out.println(msg + ": " + this.bytes + " " + this.width + "x" + this.height + "  " + this.inFile);
    }

    // Convert scaled image into buffered image
    public static BufferedImage convertToBufferedImage(Image img) {

        // Create a buffered image with transparency
        BufferedImage bi = new BufferedImage(
                img.getWidth(null), img.getHeight(null),
                BufferedImage.TYPE_INT_ARGB);

        // magic?
        Graphics2D graphics2D = bi.createGraphics();
        graphics2D.drawImage(img, 0, 0, null);
        graphics2D.dispose();

        return bi;
    }
    
    // Scale or reduce to "scale" percentage provided
    public void resize(int scale) {
        BufferedImage img = null;
        Image resizedImg = null;  

        int width = (int) (this.width * (scale/100.0) + 0.5);
        int height = (int) (this.height * (scale/100.0) + 0.5);

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            // create a new BufferedImage for drawing
            resizedImg = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        } catch (IOException e) {
            return;
        }

        try {
            ImageIO.write(convertToBufferedImage(resizedImg), this.ext, new File(resizedFile));
        } catch (IOException e) {
            return;
        }
        
        this.inFile = this.resizedFile;  // use scaled file vs original file in Class
        this.setStats();
    }
    
    // convert every pixel to an ascii character (ratio does not seem correct)
    public void convertToAscii() {
        BufferedImage img = null;
        PrintWriter asciiPrt = null;
        FileWriter asciiWrt = null;

        try {
            File file = new File(this.asciiFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        try {
            asciiPrt = new PrintWriter(asciiWrt = new FileWriter(this.asciiFile, true));
        } catch (IOException e) {
            System.out.println("ASCII out file create error: " + e);
        }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
        }

        for (int i = 0; i < img.getHeight(); i+=2) {
            for (int j = 0; j < img.getWidth(); j+=1) {
                Color col = new Color(img.getRGB(j, i));
                double pixVal = (((col.getRed() * 0.30) + (col.getBlue() * 0.59) + (col
                        .getGreen() * 0.11)));
                try {
                    asciiPrt.print(asciiChar(pixVal));
                    asciiPrt.flush();
                    asciiWrt.flush();
                } catch (Exception ex) {
                }
            }
            try {
                asciiPrt.println("");
                asciiPrt.flush();
                asciiWrt.flush();
            } catch (Exception ex) {
            }
        }
    }

    // conversion table, there may be better out there ie https://www.billmongan.com/Ursinus-CS173-Fall2020/Labs/ASCIIArt
    public String asciiChar(double g) {
        String str = " ";
        if (g >= 240) {
            str = " ";
        } else if (g >= 210) {
            str = ".";
        } else if (g >= 190) {
            str = "*";
        } else if (g >= 170) {
            str = "+";
        } else if (g >= 120) {
            str = "^";
        } else if (g >= 110) {
            str = "&";
        } else if (g >= 80) {
            str = "8";
        } else if (g >= 60) {
            str = "#";
        } else {
            str = "@";
        }
        return str;
    }

    // tester/driver
    public static void main(String[] args) throws IOException {
        Pics monaLisa = new Pics("MonaLisa", "png");
        monaLisa.printStats("Original");
        monaLisa.resize(33);
        monaLisa.printStats("Scaled");
        monaLisa.convertToAscii();

        Pics pumpkin = new Pics("pumpkin", "png");
        pumpkin.printStats("Original");
        pumpkin.resize(33);
        pumpkin.printStats("Scaled");
        pumpkin.convertToAscii();

        Pics autumn = new Pics("autumn", "png");
        autumn.printStats("Original");
        autumn.resize(33);
        autumn.printStats("Scaled");
        autumn.convertToAscii();
    }
}
Pics.main(null);
Original: 499298 389x413  images/MonaLisa.png
Scaled: 55625 128x136  images/tmp/MonaLisa.png
Original: 39392 302x265  images/pumpkin.png
Scaled: 10497 100x87  images/tmp/pumpkin.png
Original: 1071473 1522x954  images/autumn.png
Scaled: 440949 502x315  images/tmp/autumn.png
The Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click <a href='https://aka.ms/vscodeJupyterKernelCrash'>here</a> for more info. View Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details.