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 = "BlueSunset";
            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

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);
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 thats 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.main(null);
Original: 1330606 922x922  images/BlueSunset.png
Scaled: 226836 304x304  images/tmp/BlueSunset.png
---------------------------------------------------------------------------
java.lang.NullPointerException: Cannot invoke "java.awt.image.BufferedImage.getRGB(int, int)" because "<local1>" is null
	at Pics.redScale(#32:1)
	at Pics.main(#32:1)
	at .(#38:1)
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.

Setup and Getting Data

Before we write any code that color scales, we must first make a method that will get all of the data from the image that we put in. This is so that we don't rewrite the code over and over again.

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 ImageData {
    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 ImageData(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 ImageData(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;
        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
            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 scale(String color); 
}

Color Scaling

Now, moving onto the hacks. The goal is to be able to scale the colors of the image so that they are Red, Green, or Blue. Rather than creating 3 separate methods to do this, we can rather just make a method that does all 3 at the same time! We can do this by specifying which color we want it to be scaled to (red, green, or blue) and then have a different output for each. Also, I added gray scaling as well. To do this, you would just take the average of all of the RGB values and then set that as the R, G, and B value of that particular color cell. If you want to do ASCII, input ASCII into the method so that the method converts the image to ASCII.

package _notebooks;

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

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

import javax.imageio.ImageIO;

public class ColorScale extends ImageData {
    
    public ColorScale(String name, String color, String ext) {
        super(name, ext);
        this.colorFile = this.outDir + name + color + ".png";
    }

    public ColorScale(String name, String color) {
        super(name, color);
        this.colorFile = this.outDir + name + color + ".png";
    }

    @Override
    protected void scale(String color) {
        BufferedImage img = null;
        PrintWriter asciiPrt = null;
        FileWriter asciiWrt = null;
        Color col = null;
        
        
        // Deletes previously named file if exists 
        try {
            File file = new File(this.colorFile);
            Files.deleteIfExists(file.toPath()); 
        } catch (IOException e) {
            System.out.println("Delete File error: " + e);
        }

        try {
            img = ImageIO.read(new File(this.inFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (color.equals("Red")){
            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);
                }
            }
        }

        if (color.equals("Blue")){
            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);
                }
            }
        }

        if (color.equals("Green")){
            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);
    
                }
            }
        }

        if (color.equals("Gray")){
            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);
                }
            }
        }

        if(color.equals("ASCII")){
            final int XLENGTH = 1;
            final int YLENGTH = 2;

            for (int i = 0; i < img.getHeight(); i += 4) {
                for (int j = 0; j < img.getWidth(); j += 2) {
                    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 {
            ImageIO.write(img, "png", new File(this.colorFile) );
        } catch (IOException e) {
            e.printStackTrace();
        }

        
    }

    public static void main(String[] args) {
        ColorScale BlueSunset = new ColorScale("BlueSunset", "png");
        BlueSunset.scale("Red");
        BlueSunset.scale("Green");
        BlueSunset.scale("Blue");
        BlueSunset.scale("Gray");
    }

}

Click on the links if you want to see the original image, the Red scaled image, the Green scaled image, the Blue scaled image, and the Gray scaled image. As for the ASCII, it was coded but the output is very scuffed and does not appeal to the eyes so I'm not going to show it (for your own good I assure you). The picture is very monotone and that causes it to not have the best ASCII output.