小玩意|图片转ASCII

2022-11-21 20:32:30 浏览数 (1)

今天,我们来完成一个小玩意,将图片转成ASCII,最后使用Base64转换成灰色图。如,将图

转换成下图的模样。

步骤

主要包含如下几个步骤:

  • 读取图片文件到BufferedImage, 如:
代码语言:javascript复制
BufferedImage sourceBufferedImage = ImageIO.read(new File(sourceFileFile))
  • 创建画布并进行灰度处理 - 获取像素点的灰度值,将灰度值的深浅转换成自己定义的ASCII字符
代码语言:javascript复制
  BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics g = bufferedImage.createGraphics();
代码语言:javascript复制
    for (int i = miny; i < height; i  = steps) {
      for (int j = minx; j < width; j  = steps) {
        /**
         * <pre>
         * 像素灰度化:
         * 
         * 灰度化,在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色, 
         * 其中R=G=B的值叫灰度值,
         * 因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值), 
         * 灰度范围为0-255
         * 
         * </pre>
         */
        int pixel = bi.getRGB(j, i);
        int red = (pixel & 0xff0000) >> 16;
        int green = (pixel & 0xff00) >> 8;
        int blue = (pixel & 0xff);

        /**
         * <pre>
         * 典型处理公式:0.299f * red   0.578f * green   0.114f * blue
         * 
         * 根据重要性及其它指标,将三个分量以不同的权值进行加权平均。
         * 由于人眼对绿色的敏感最高,对蓝色敏感最低,
         * 因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像。
         * </pre>
         */
        float gray = 0.299f * red   0.578f * green   0.114f * blue;

        int index = Math.round(gray * (CANDIDATE_CHARS.length()   1) / 255);
        // 如果超过使用空格代替
        String c = index >= CANDIDATE_CHARS.length() ? " " : String.valueOf(CANDIDATE_CHARS.charAt(index));
        g.drawString(c, j, i);
        System.out.print(c);
      }
      System.out.println();
    }
  • 获取输出图片的Base64字符串
代码语言:javascript复制
  private static String encodeBase64(BufferedImage targetBufferedImage) {
    try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
      ImageIO.write(targetBufferedImage, "jpg", out);
      String base64 = org.apache.commons.codec.binary.Base64.encodeBase64String(out.toByteArray());
      return base64;
    } catch (IOException e) {
      logger.error("ImageIO.write error", e);
    }
    return null;
  }
  • Base64字符串转图片
代码语言:javascript复制
  File fileImage = new File(targetFilePath);
    byte[] decodedBytes = org.apache.commons.codec.binary.Base64.decodeBase64(fileBase64String);
    FileUtils.writeByteArrayToFile(fileImage, decodedBytes);

完整代码:

代码语言:javascript复制
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ImageToAsciiHandler {

  private static Logger logger = LoggerFactory.getLogger(ImageToAsciiHandler.class);

  // 自定义候选使用的字符串
  private static final String CANDIDATE_CHARS = "MN@& @~oC!:.";

  public static void generate(String sourceFileFile, String targetFilePath, int skipSteps) throws IOException {

    /**
     * 1、读取原图片文件
     */
    BufferedImage sourceBufferedImage = ImageIO.read(new File(sourceFileFile));

    /**
     * 2、写入BufferedImage
     */
    BufferedImage targetBufferedImage = writeInBufferImage(sourceBufferedImage, skipSteps);

    /**
     * 3、获取图片文件的Base64字串
     */
    String fileBase64String = encodeBase64(targetBufferedImage);

    /**
     * 4、Base64转图片
     */
    File fileImage = new File(targetFilePath);
    byte[] decodedBytes = org.apache.commons.codec.binary.Base64.decodeBase64(fileBase64String);
    FileUtils.writeByteArrayToFile(fileImage, decodedBytes);
  }

  private static String encodeBase64(BufferedImage targetBufferedImage) {
    try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
      ImageIO.write(targetBufferedImage, "jpg", out);
      String base64 = org.apache.commons.codec.binary.Base64.encodeBase64String(out.toByteArray());
      return base64;
    } catch (IOException e) {
      logger.error("ImageIO.write error", e);
    }
    return null;
  }

  private static BufferedImage writeInBufferImage(BufferedImage bi, int skipSteps) {
    int width = bi.getWidth();
    int height = bi.getHeight();
    int minx = bi.getMinX();
    int miny = bi.getMinY();
    int steps = 1   skipSteps;
    BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics g = bufferedImage.createGraphics();
    g.setColor(null);
    g.fillRect(0, 0, width, height);
    g.setColor(Color.BLACK);
    g.setFont(new Font("微软雅黑", Font.PLAIN, steps));

    for (int i = miny; i < height; i  = steps) {
      for (int j = minx; j < width; j  = steps) {
        /**
         * <pre>
         * 像素灰度化:
         * 
         * 灰度化,在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色, 
         * 其中R=G=B的值叫灰度值,
         * 因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值), 
         * 灰度范围为0-255
         * 
         * </pre>
         */
        int pixel = bi.getRGB(j, i);
        int red = (pixel & 0xff0000) >> 16;
        int green = (pixel & 0xff00) >> 8;
        int blue = (pixel & 0xff);

        /**
         * <pre>
         * 典型处理公式:0.299f * red   0.578f * green   0.114f * blue
         * 
         * 根据重要性及其它指标,将三个分量以不同的权值进行加权平均。
         * 由于人眼对绿色的敏感最高,对蓝色敏感最低,
         * 因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像。
         * </pre>
         */
        float gray = 0.299f * red   0.578f * green   0.114f * blue;

        int index = Math.round(gray * (CANDIDATE_CHARS.length()   1) / 255);
        // 如果超过使用空格代替
        String c = index >= CANDIDATE_CHARS.length() ? " " : String.valueOf(CANDIDATE_CHARS.charAt(index));
        g.drawString(c, j, i);
        System.out.print(c);
      }
      System.out.println();
    }
    g.dispose();
    return bufferedImage;
  }

}

测试一下:

代码语言:javascript复制
import java.io.IOException;

public class Main {

  public static void main(String[] args) throws IOException {
    
    for(int skipSteps = 3; skipSteps <= 3; skipSteps  ) {
       ImageToAsciiHandler.generate("/Users/xxxx/Desktop/katong.png", "/Users/td/Desktop/xxx"  skipSteps  ".jpg", skipSteps);
      
    }
    
  }

}

运行就能等到转换后的图。

像素处理

如果每个像素处理,会发现图转换后,具有较多的阴影。所以,在处理的时候,增加了skipStep的参数,用于过滤掉像素处理。

大家可以通过一个for循环进行尝试。

代码语言:javascript复制
import java.io.IOException;

public class Main {

  public static void main(String[] args) throws IOException {
    
    for(int skipSteps = 0; skipSteps <= 7; skipSteps  ) {
       ImageToAsciiHandler.generate("/Users/xxxx/Desktop/katong.png", "/Users/td/Desktop/xxx"  skipSteps  ".jpg", skipSteps);
      
    }
    
  }

}

发现skipStep ==3的时候,效果还行,不过也要看指定的ASCII字符串。

再对一张100元的纸币进行一次测试:

过滤不同像素点有不一样的显示,如:

有兴趣的同学可以试试。今天的例子只有一张图,后续将多张图组成的GIF处理补充上。

0 人点赞