登录
原创

为小姐姐画的字符画

发布于 2020-10-15 阅读 254
  • Java
原创

我的画画梦

image.png

 我本该是一个会画画的人。
 我的画里有山川、河流与大海,有麦田、向日葵和白云。
 那时候,我坐在小镇东面河堰的草堆上,脚下的草地,像绿色的瀑布,从脚下高高的河堰上一直流淌到远方的河边,牛儿棕色的绒毛像火烧云,成群的羊儿像白云。
 一张纸,一支画笔,一盒颜料。到了傍晚,牛羊就从这片草地上跑到了我的纸上。
 这方面的天赋,让我一直在等着北京那两所著名的大学的录取通知。
 直到家里有了电视机之后, 我在《新闻联播》之后的《天气预报》里面看到了北京的天气情况,才知道北京的冬天有多冷,这个念头从此打消。由此我也开始了南下搬砖的生活。但是画画这个梦想就像火种一样,风一来,他就要燃起来。
 苏州是一个河流纵横交错,湖泊众多的城市,我时常沿着那些稻田边行走,一点也不害怕陷入那些泥地里面。累了我就坐下休息一会。
 这时候一个小姐姐也从稻田的另一头走了过来,越来越近,我用手机给她拍了个照片。放大一看,惊为天人,说实话,在苏州我从来没见过这样的小姐姐。

image.png

待我抬头再去看时,她已经走远。我决定重拾画笔,为她画一幅画。历时两年,我终于在记事本里面敲出来了。

image.png

如果你觉得看不清,那很正常,因为我把像素缩小了5倍,因为如果按照原来的像素来放这些字符,已经超过了一篇文章的字数限制,来看看预览图

image.png

画画的准备材料

  • 图像分析学,了解色彩的组成
  • jdk1.8
  • javafx
  • 一张小姐姐的绝美照片
  • 美好的周末的空闲时刻
  • 一个装好了java开发环境的笔记本电脑
  • 移位操作
  • 按位与
  • 按位或
  • 计算机组成原理
  • 灰度处理

计算机组成原理知识复习

  • 一个无符号int类型的变量,在计算机内部占4个字节,每个字节占8个bit
  • &这是一个按位与操作,它是一个双目运算符,它标示输入的两个值在都为1的情况下,输出才会为1,按位与操作的功能其实是一个过滤器,为什么这样说呢,请看下面这个表格
输入a 输入b 按位与的结果
0 0 0
0 1 0
1 0 0
1 1 1

为什么我说它是一个过滤器呢,因为如果我手里有一个网兜,我想向水里捞一把,如果水里空空如也,那么我捞起的就是空,而如果水里有一条大鱼,我就能捕获一条鱼。那么我此刻手里握着的是一个1,那么只有我遇上1的时候,我才会微笑,因为它是我确认过的眼神。这里需要格外注意,只有最后那一行才是我们需要的,当然,这里你肯定还不知道为什么,别急,继续往下看。

|这是一个按位或操作,他的功能是只要两个输入之中只要有一个1,那么就输出1,我忘记了代码里是否需要,先写在这里吧,看下面的表格

输入a 输入b 输出结果
0 0 0
0 1 1
1 0 1
1 1 1
  • >>这个操作符号不是两个大于号,它是移位操作,向哪里移位呢,看它的箭头方向指向何方,我想我们都是幼儿园顺利毕业的朋友,知道是箭头的方向是向右,如果实在不知道指向哪里,你就想想你在电脑上打字的时候,光标移动的方向是向哪里的,为什么介绍这个因为我们真的用得到,可以不用吗?抱歉,不用这个我不会玩,看下面的知识复习,先看一个int类型的二进制数的伪代码
int a = 10000001,10010100,01010101,010100101;

接下里,我要进行一些操作

//第一步:
a & 0xff
//输出:010100101

//第二步:
a >> 8 & 0xff
//输出:01010101

//第三步:
a >> 16 & 0xff
//输出:10010100

//第四步:
a >> 24 & 0xff
//输出:10000001

这些操作的目的是,从低位到高位,每次获取8个bit的值。
先记住这些,以后考试要考的。

色彩分析知识复习

我们都知道在计算机中,一个图片实际上就是有若干个小点点组成的,这个小点点我们通常叫做像素,而一个像素我们通常认为是有红黄蓝三种颜色组成,当然还有透明度,红黄蓝和透明度,我们通常认为都是由一个0-255内的数字组成的,即一个字节的长度就可以表示出来,那么天然的就可以用一个int值来表示透明度和红黄蓝了,即argb分别用一个字节来表示,合起来就是一个int值,那么我们看到的2829929这个值有可能就是一个颜色咯。即最高八位表示透明度,然后次高八位表示红色,最低位表示蓝色,次低八位表示绿色,请看下面的表示

10101010,10101010,10101010,10101010
      a             r          g             b 
 透明度        红色     绿色         蓝色

灰度处理

如果你听说过灰度处理,跳过这一节,如果没听过,那就坐好听一下。

灰度处理是将一个颜色的红色的部分提取21%,绿色部分提取71%,蓝色部分提取7%得到的一个数值

别问我是怎么知道的,我只能告诉你这是我用颜料盒调了很多次才提炼出来的配方,你不需要知道原理,只要按照这个配方进行调制,炒菜的味道就会很香。

像素转化为字符

灰度处理过的图片,我们认为只有两种颜色了,但是只有两种颜色的画,失真率会特别高,除非你的照片原来就是黑白的,即使是黑白的照片,也是灰度处理过的,除非你是用画笔的黑色和白色画出来的。

为了让我们的字符画的细节更加丰富,对比不是那么强烈,我们需要让不同的像素转化成明暗不同的字符。我们通过程序获取的int颜色值,通过灰色处理后,只剩下一个byte范围的数值,即0-255,当然你可以找256个字符用来表示每一个灰度值,这样的效果理论上来说是效果最好的,但是我还没有发现细节变化线性变化这么好的256个字符。

我的策略是利用除法进行分组,得到这个灰度值应该大约用到哪一个字符,这在String getChar(int value) 这个方法中可以体现

全部代码,不需要打赏作者就能出现

package lesson10;

import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.text.SimpleDateFormat;
import java.util.regex.Pattern;
public class Login extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        //Image image = new Image("file:/Users/java0904/Pictures/three.jpeg",500,500,false,true);
        Image image = new Image("file:/Users/java0904/Pictures/taile.jpg");
        ImageView imageView = new ImageView(image);
        PixelReader pixelReader = image.getPixelReader();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < image.getHeight(); i += 1) {
            for (int j = 0; j < image.getWidth(); j += 1) {
                //此处的取值是j,i,因为向txt文件中写入字符串的 时候,是需要一行一行写的,不是一列一列写入的
                Color color = pixelReader.getColor(j, i);
                int argb = pixelReader.getArgb(j, i);
                int a = argb >>> 24;
                int r = (argb >> 16) & 0xff;
                int g = (argb >> 8) & 0xff;
                int b = (argb) & 0xff;

                //int value = (int) ((color.getRed() * 30 + color.getGreen() * 59 + color.getBlue() * 11) / 100 * 255);
                //Color类中的color.grayscale()方法的参数值
                int value = (int) ((color.getRed() * 21 + color.getGreen() * 71 + color.getBlue() * 7) / 100 * 255);
                value = (int) (r * 0.21 + g * 0.71 + b * 0.07);
                sb.append(getChar(value));
            }
            sb.append("\r\n");
        }
        write(sb);
        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(imageView);
        Scene scene = new Scene(borderPane);
        primaryStage.setScene(scene);
        primaryStage.setWidth(800);
        primaryStage.setHeight(800);

        primaryStage.setTitle("javafx practise");
        primaryStage.show();
    }

    private String getChar(int value) {
        String s = "龖齱龠馬車瓦日乙卜丶";
        //String s = "龖齱龠馬車瓦MNHBEYUO*!";

        int length = s.length();

        int index = value / (256 / length + 1);
        char c = s.charAt(index);
        return c + "";
    }

    private void write(StringBuilder sb) throws Exception {
        //System.out.println(sb.toString());
        File file = new File("/Users/java0904/Pictures/taile.txt");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
        bufferedWriter.write(sb.toString());
        bufferedWriter.close();
    }

}

debug

如果你直接运行上面的代码,可以肯定的是会报错,这是因为你必须定义好自己的图片的地址,以及生成字符文件的地址

如何查看你的字符画

当你的应用程序顺利运行之后,你的javafx应用正常加载并且显示了一个小姐姐的图片之后,你在字符生成的地址中应该会看到那个txt文件,你可以用记事本打开看看,一般来说,如果你的图片很大(一般指分辨率很高)的话,你这个文件的字符也是超级多的,你打开之后看到的只能是局部,此时,你应该把字体缩小到一定的程度就可以看到全貌了,如果你是mac os的话,你可以用command -这两个键来调整大小。

评论区

眉上的汗水,眉下的泪水,你总要选择一样

6

1

2