Goals

  • [x] Fix issue of elongation with ASCII - Fixed as best as possible, though some distortion still occurs.
  • [x] Try to convert images into Grey Scale, Red Scale, Blue Scale, and Green Scale.
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 final String greyDir = "images/tmp/greyscale/";
    private final String redDir = "images/tmp/red/";
    private final String blueDir = "images/tmp/blue/";
    private final String greenDir = "images/tmp/green/";
    private String inFile;
    private String resizedFile;
    private String redFile; private String greenFile; private String blueFile;
    private String greyscaleFile;
    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.greyscaleFile = this.greyDir + name + "_grey" + "." + ext;
        this.redFile = this.redDir + name + "_red" + "." + ext;
        this.blueFile = this.blueDir + name + "_blue" + "." + ext;
        this.greenFile = this.greenDir + name + "_green" + "." + 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();
    }

    public void greyscale() {
        BufferedImage img = null;
        BufferedImage greyImg = null;  

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            greyImg = img;
            // create a new BufferedImage for drawing
            for (int i = 0; i < img.getHeight(); i++) {
                for (int j = 0; j < img.getWidth(); j++) {
                    Color co = new Color(img.getRGB(j, i));
                    int red = (int) (co.getRed() * 0.299);
                    int green = (int) (co.getGreen() * 0.587);
                    int blue = (int) (co.getBlue() * 0.114);
                    int total = red + green + blue;
                    Color newColor = new Color(
                        total, total, total
                    );
                    greyImg.setRGB(j, i, newColor.getRGB());
                }
            }

        } catch (IOException e) {
            return;
        }

        try {
            ImageIO.write(convertToBufferedImage(greyImg), this.ext, new File(greyscaleFile));
        } catch (IOException e) {
            return;
        }
        
        this.inFile = this.greyscaleFile;  // use scaled file vs original file in Class
        this.setStats();
    }
    
    public void redscale() {
        BufferedImage img = null;
        BufferedImage redImg = null;  

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            redImg = img;
            // create a new BufferedImage for drawing
            for (int i = 0; i < img.getHeight(); i++) {
                for (int j = 0; j < img.getWidth(); j++) {
                    Color co = new Color(img.getRGB(j, i));
                    int red = (int) co.getRed();
                    int green = 0;
                    int blue = 0;
                    Color newColor = new Color(
                            red,
                            green,
                            blue);
                    redImg.setRGB(j, i, newColor.getRGB());
                }
            }

        } catch (IOException e) {
            return;
        }

        try {
            ImageIO.write(convertToBufferedImage(redImg), this.ext, new File(redFile));
        } catch (IOException e) {
            return;
        }
        
        this.inFile = this.redFile;  // use scaled file vs original file in Class
        this.setStats();
    }

    public void greenscale() {
        BufferedImage img = null;
        BufferedImage greenImg = null;  

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            greenImg = img;
            // create a new BufferedImage for drawing
            for (int i = 0; i < img.getHeight(); i++) {
                for (int j = 0; j < img.getWidth(); j++) {
                    Color co = new Color(img.getRGB(j, i));
                    int red = 0;
                    int green = (int) co.getGreen();
                    int blue = 0;
                    Color newColor = new Color(
                            red,
                            green,
                            blue);
                    greenImg.setRGB(j, i, newColor.getRGB());
                }
            }

        } catch (IOException e) {
            return;
        }

        try {
            ImageIO.write(convertToBufferedImage(greenImg), this.ext, new File(greenFile));
        } catch (IOException e) {
            return;
        }
        
        this.inFile = this.greenFile;  // use scaled file vs original file in Class
        this.setStats();
    }

    public void bluescale() {
        BufferedImage img = null;
        BufferedImage blueImg = null;  

        try {
            // read an image to BufferedImage for processing
            img = ImageIO.read(new File(this.inFile));  // set buffer of image data
            blueImg = img;
            // create a new BufferedImage for drawing
            for (int i = 0; i < img.getHeight(); i++) {
                for (int j = 0; j < img.getWidth(); j++) {
                    Color co = new Color(img.getRGB(j, i));
                    int red = (int) (co.getRed() * 0.299);
                    int green = (int) (co.getGreen() * 0.587);
                    int blue = (int) (co.getBlue() * 0.114);
                    int total = red + green + blue;
                    Color newColor = new Color(0, 0, total);
                    blueImg.setRGB(j, i, newColor.getRGB());
                }
            }

        } catch (IOException e) {
            return;
        }

        try {
            ImageIO.write(convertToBufferedImage(blueImg), this.ext, new File(blueFile));
        } catch (IOException e) {
            return;
        }
        
        this.inFile = this.blueFile;  // 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++) {
            if (i % 2 != 0) {
                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.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.greyscale();
        //monaLisa.redscale();
        //monaLisa.greenscale();
        monaLisa.bluescale();
        // monaLisa.printStats("Original");
        // monaLisa.resize(33);
        // monaLisa.printStats("Scaled");
        // monaLisa.convertToAscii();

        Pics pumpkin = new Pics("pumpkin", "png");
        // pumpkin.greyscale();
        // pumpkin.redscale();
        // pumpkin.greenscale();
        pumpkin.bluescale();
        // pumpkin.printStats("Original");
        // pumpkin.resize(33);
        // pumpkin.printStats("Scaled");
        // pumpkin.convertToAscii();
    }
}
Pics.main(null);

Results

Different colorations

greyimage.png redimage.png blueimage.png greenimage.png


greyimage.png redimage.png blueimage.png greenimage.png


  • I do have to acknowledge a technical limitation, which is that the red, blue, and green commands cannot be run at the same time as they cancel each other out. I cannot currently find a fix for it. If I do find a fix, I will implement it ASAP.

ASCII

  • Mostly fixed issue of ascii stretching

monalisaascii.png pumpkinascii.png

Final Note

  • Though this was pretty useful to figure out how imageIO worked, I don't see a particular use for it in the website about Kpop/Korean Culture
    • The issue I see is that this image manipulation has to be done through the backend, which would make any sort of user input feel slow as they would have to wait for the page to refresh to display a changed version.