【问题标题】:How to draw an image using ASCII symbols?如何使用 ASCII 符号绘制图像?
【发布时间】:2019-05-12 14:58:48
【问题描述】:

我正在尝试从图像制作 ASCII 艺术,但由于某种原因,输出总是旋转,我多次检查我的代码,但我根本找不到错误。我猜这与imageWidthimageHeight 有关,但对我来说一切都很好。

代码可以在github找到

【问题讨论】:

    标签: java ascii-art


    【解决方案1】:

    从图像中绘制 ASCII 艺术作品

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

    原图:

    ASCII图片:


    此代码从文件中读取图像,将其缩小到高度的 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 JavaConvert image to ASCII art

    【讨论】:

      【解决方案2】:

      在 ImageWriting.java 中,第 34 行:

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          【解决方案4】:

          创建字符密度和亮度图

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

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

          图片:

          Original picture ASCII: 0 - 255
          0x0000 - 0x00FF
          Runic:
          0x16A0 - 0x16FF
          Box Drawing:
          0x2500 - 0x257F
          Block Elements:
          0x2580 - 0x259F
          Geometric Shapes:
          0x25A0 - 0x25FF
          Hiragana:
          0x3040 - 0x309F

          密度标度:

          Unicode block Range of characters Density scale
          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;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-05-26
            • 2014-05-05
            • 2012-07-27
            • 1970-01-01
            • 2020-10-28
            • 2014-09-25
            • 1970-01-01
            • 2018-09-22
            相关资源
            最近更新 更多