尝试使用ASCII字母绘制图像,但我的图像旋转出来

时间:2019-05-12 14:58:49

标签: java ascii-art

我正在尝试从图像制作ASCII Art,但是由于某种原因,输出总是旋转的,我经过了很多遍代码,但我根本找不到错误。我想这与imageWidth和imageHeight有关,但对我来说一切都很好。

代码可以在github

上找到

4 个答案:

答案 0 :(得分:2)

从图像中绘制 ASCII 艺术

假设一个字符占据了 21×8 像素的区域。所以首先你要把原图缩小,得到这个区域的平均颜色,然后得到这个颜色的平均亮度,再转换成字符。

原图:

Original picture

ASCII 图片:

ASCII picture


此代码从文件中读取图像,将其缩小到高度的 1/21 和宽度的 1/8,计算缩放区域的平均颜色,然后计算每种颜色的平均亮度并选择一个对应密度的字符,然后将这些字符保存到一个文本文件中。

不缩放scH=1scW=1,字符数等于原图的像素数。

public static void main(String[] args) throws IOException {
  // assume that one character occupies an area of 21×8 pixels
  char[][] chars = readImage("/tmp/image.jpg", 21, 8);
  writeToFile("/tmp/image.txt", chars);
}

static char[][] readImage(String path, int scH, int scW) throws IOException {
  BufferedImage image = ImageIO.read(new File(path));
  int height = image.getHeight() / scH;
  int width = image.getWidth() / scW;
  char[][] chars = new char[height][width];
  for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
      // scaling image and accumulating colors
      int colorRGB = 0;
      for (int k = 0; k < scH; k++)
        for (int p = 0; p < scW; p++)
          colorRGB += image.getRGB(j * scW + p, i * scH + k);
      // get the average color
      Color color = new Color(colorRGB / (scH * scW));
      // read the R, G, B values of the color and get the average brightness
      int brightness = (color.getRed() + color.getGreen() + color.getBlue()) / 3;
      // get a character depending on the brightness value
      chars[i][j] = getDensity(brightness);
    }
  }
  return chars;
}

static final String DENSITY =
      "@QB#NgWM8RDHdOKq9$6khEPXwmeZaoS2yjufF]}{tx1zv7lciL/\\|?*>r^;:_\"~,'.-`";

static char getDensity(int value) {
  // Since we don't have 255 characters, we have to use percentages
  int charValue = (int) Math.round(DENSITY.length() / 255.0 * value);
  charValue = Math.max(charValue, 0);
  charValue = Math.min(charValue, DENSITY.length() - 1);
  return DENSITY.charAt(charValue);
}

static void writeToFile(String path, char[][] chars) throws IOException {
  FileWriter writer = new FileWriter(path);
  for (char[] row : chars) {
    String str = String.valueOf(row);
    writer.append(str).write("\n");
    System.out.println(str);
  }
  writer.flush();
  writer.close();
}

输出:

***************************************
***************************************
*************o/xiz|{,/1ctx*************
************77L*```````*_1{j***********
**********?i```````````````FZ**********
**********l`````````````````7**********
**********x`````````````````L**********
**********m?i`````````````iz1**********
************]x```````````\x{***********
********?1w]c>```````````La{]}r********
******jSF~```````````````````^xv>******
*****l1,```````````````````````*Sj*****
****7t```````````````````````````v7****
***uL`````````````````````````````t]***

另见:Restore an image from an ASCII art with Java

答案 1 :(得分:0)

在ImageWriting.java中,第34行:

this.writer.append(Density.DENSITY.getDensityFor(this.brightnessValues[j][i]));

答案 2 :(得分:0)

我强烈怀疑您正在遍历某个 for 循环中的一个坐标,而另一个循环嵌套在其中。 (不会在另一个网站上追逐代码。)

尝试交换 for 循环的嵌套或按数组索引访问元素的顺序(根据您拥有的任何代码,将 [i][j] 代码片段替换为 [j][i] 或类似代码另一个我不会去追求的网站)。

答案 3 :(得分:0)

创建字符密度和亮度图

您可以从任何字符范围创建自己的字符密度图,然后,由于密度分布不​​均匀,将其转换为亮度图,然后进一步调用 ceilingEntryfloorEntry 方法。

首先使用 java.awt 包将每个字符绘制为黑白图片并计算像素数 - 您会得到一个密度图。然后,对于该地图中的每个条目,计算比例 0-255 上的亮度百分比 - 您会得到一个亮度图

图片:

<头>
原图 ASCII:0 - 255
0x0000 - 0x00FF
符文:
0x16A0 - 0x16FF
方框图:
0x2500 - 0x257F
块元素:
0x2580 - 0x259F
几何形状:
0x25A0 - 0x25FF
平假名:
0x3040 - 0x309F
enter link description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

密度标度:

<头>
Unicode 块 字符范围 密度标度
ASCII
0-255
¶@ØÆMåBNÊßÔR#8Q&mÃ0À$GXZA5ñk2S%±3Fz¢yÝCJf1t7ªLc¿+?(r/¤²!*;"^:,'.` 
Runic
0x16A0-0x16FF
ᛥᛤᛞᚥᚸᛰᛖᚻᚣᛄᚤᛒᚢᚱᛱᚷᚫᛪᚧᚬᚠᛏᚨᚰᚩᚮᚪᚳᚽᚿᛊᛁᛵᛍ᛬ᚲᛌ᛫
Box Drawing
0x2500-0x257F
╬╠╫╋║╉╩┣╦╂╳╇╈┠╚┃╃┻╅┳┡┢┹╀╧┱╙┗┞┇┸┋┯┰┖╲╱┎╘━┭┕┍┅╾│┬┉╰╭╸└┆╺┊─╌┄┈╴╶
Block Elements
0x2580-0x259F
█▉▇▓▊▆▅▌▚▞▀▒▐▍▃▖▂░▁▏
Geometric Shapes
0x25A0-0x25FF
◙◘■▩●▦▣◚◛◕▨▧◉▤◐◒▮◍◑▼▪◤▬◗◭◖◈◎◮◊◫▰◄◯□▯▷▫▽◹△◁▸▭◅▵◌▱▹▿◠◃◦◟◞◜
Hiragana
0x3040-0x309F
ぽぼゑぜぬあおゆぎゐはせぢがきぱびほげばゟぁたかぞぷれひずどらさでけぉちごえすゎにづぇとょついこぐうぅぃくっしへゞゝ゚゙

代码:

public static void main(String[] args) {
  Font font = new Font(Font.MONOSPACED, Font.PLAIN, 22);
  // ASCII characters: 0 - 255
  // Runic: 0x16A0 - 0x16FF
  // Box Drawing: 0x2500 - 0x257F
  // Block Elements: 0x2580 - 0x259F
  // Geometric Shapes: 0x25A0 - 0x25FF
  // Hiragana: 0x3040 - 0x309F
  TreeMap<Integer, List<String>> density = getDensityMap(font,0x25A0,0x25FF,0);
  // the map of brightness of pixels [0, 255]
  TreeMap<Integer, List<String>> brightness = getBrightnessMap(density);
  // output, geometric shapes
  for (List<String> value : brightness.values()) System.out.print(value.get(0));
  // ◙◘■▩●▦▣◚◛◕▨▧◉▤◐◒▮◍◑▼▪◤▬◗◭◖◈◎◮◊◫▰◄◯□▯▷▫▽◹△◁▸▭◅▵◌▱▹▿◠◃◦◟◞◜
}

/**
 * @param density character density map
 * @return the pixel brightness map [0, 255],
 * based on percentages of character density
 */
static TreeMap<Integer, List<String>> getBrightnessMap(
        TreeMap<Integer, List<String>> density) {
  int max = density.lastKey(); // maximum density
  TreeMap<Integer, List<String>> brightness = new TreeMap<>();
  for (Map.Entry<Integer, List<String>> entry : density.entrySet()) {
    // pixel brightness, based on the percentage of character density
    int key = (int) Math.round(255.0 - entry.getKey() * 255.0 / max);
    List<String> value = entry.getValue();
    List<String> val = brightness.remove(key);
    if (val == null) val = new ArrayList<>();
    val.addAll(value);
    brightness.put(key, val);
  }
  return brightness;
}

/**
 * @param f   font to render text
 * @param min character codepoint range, lower bound
 * @param max character codepoint range, upper bound
 * @param pd  padding as a precaution, in most cases 0
 * @return the character density map:
 * key - density, value - list of characters
 */
static TreeMap<Integer, List<String>> getDensityMap(
        Font f, int min, int max, int pd) {
  // key - density, value - list of characters
  TreeMap<Integer, List<String>> density = new TreeMap<>();
  for (int i = min; i <= max; i++) {
    // printable characters
    if (f.canDisplay(i) && Character.isDefined(i)
          && !Character.isISOControl(i)
          && !Character.isIdentifierIgnorable(i)) {
      String str = String.valueOf(Character.toChars(i));
      int key = getDensity(str, f, pd);
      List<String> list = density.remove(key);
      if (list == null) list = new ArrayList<>();
      list.add(str);
      density.put(key, list);
    }
  }
  return density;
}

/**
 * @param text source text to draw
 * @param f    font to render text
 * @param pd   padding as a precaution, in most cases 0
 * @return the density of the characters in this text
 */
static int getDensity(String text, Font f, int pd) {
  FontRenderContext ctx = new FontRenderContext(f.getTransform(), false, false);
  Rectangle bounds = f.getStringBounds(text, ctx).getBounds();
  int width = bounds.width + pd * 2;
  int height = bounds.height + pd * 2;
  BufferedImage image =
          new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
  Graphics2D graphics = (Graphics2D) image.getGraphics();
  graphics.setFont(f);
  graphics.drawString(text, pd + bounds.x, pd - bounds.y);
  //ImageIO.write(image, "png", new File("text.png"));
  int density = 0;
  for (int i = 0; i < height; i++)
    for (int j = 0; j < width; j++)
      if (image.getRGB(j, i) == 0xFFFFFFFF)
        density++;
  return density;
}