@

背景

用Java实现字符拼成一个图片,先看一下效果:

  • 左边是原图,右边是用字符拼成的效果,即写好代码,读入一张图片,然后用指定的字符把这个图片的轮廓拼出来



    放大之后是这个样子
  • 用love拼成了哆啦A梦的轮廓

源码

public class AscPic {
public static void main(String[] args) throws IOException {
String path = "d:/qqq.png";//导入的图片
String base = "love";//将会用这个字符串里的字符填充图片
BufferedImage image = ImageIO.read(new File(path));//读入图片,并用图片缓冲区对象来接收 //双层for循环,遍历图片
for (int y = 0; y < image.getHeight(); y++) {//先竖向遍历,再横向遍历,即一行一行的找,后面也会一行一行的打印
for (int x = 0; x < image.getWidth(); x++) {
int color = image.getRGB(x, y);//图片缓冲区自带的方法,可以得到当前点的颜色值,返回值是int类型
int r=(color>>16)&0xff;
int g=(color>>8)&0xff;
int b=color&0xff;
float gray = 0.299f * r + 0.578f * g + 0.114f * b;//灰度值计算公式,固定比例,无需理解
int index = Math.round(gray * (base.length()) / 255);
if(index>=base.length()) {
System.out.print(" ");//白色的地方打空格,相当于白色背景,这样图片轮廓比较明显
}else {
System.out.print(base.charAt(index));//有颜色的地方打字符
}
}
System.out.println();//一行打完,换行
}
}
}

代码思路

整体思路为导入想好处理的图片,遍历,得到每个像素点的颜色,然后将其转换成灰度值(也就是把彩色转换成黑白),根据得到的灰度值计算字符串索引,达到效果就是不同颜色可以对应不同的下标,以此来匹配字符串中的字符,最后按照原有的坐标点把这些字符打印出来即可

  1. 定义好想要填充的字符串
  2. 导入想要处理的图片,需要用到BufferedImage(图片缓冲区)这个对象
  3. 遍历整张图片,这里需要注意,外层循环遍历y轴,内层遍历x轴,因为打印的时候需要一行一行打印,打完一行要换行
  4. 根据getRGB(x,y)方法,传入当前的坐标点,得到当前点的颜色
  5. 从得到的颜色中单独拆分出r,g,b的值
  6. 根据得到的rgb计算对应的灰度值
  7. 根据灰度值计算索引
  8. 打印

难点讲解

如何得到rgb

int color = image.getRGB(x, y);//图片缓冲区自带的方法,可以得到当前点的颜色值,返回值是int类型
int r=(color>>16)&0xff;
int g=(color>>8)&0xff;
int b=color&0xff;

首先明确一点,所谓的rgb就是三原色,red,green,blue,无论是ps还是程序,都会通过给rgb赋值来拼成一个新的颜色。通过etRGB(x,y)得到的颜色是一个int类型,我们用color来表示。这个值包含四部分内容,分别为a,r,g,b,即透明度,red,green,blue。每一部分恰好占一个字节。所以我们要做的就是从这个int中去单独得到从左数第二,第三,第四字节的数值。

怎么做呢?先来回顾两个位运算的基本知识:

  1. "&",与,都是0则结果为0,都是1结果为1,一个1一个0结果为0.从数学上理解,&操作符其实是在取交集。

    7&5=?

    首先换算成二进制,7的二进制是0111,5的二进制是0101

    0111&0101=0101,还是5。

    通过上面的计算有没有发现一个规律,如果我想要让一个数和另一个数&完结果还是这个数本身怎么办?比如我想让0101,0111,0011和一个数&完结果还是他们本身,那么这个数应该是多少。
  2. ">>",右移.

    8>>2,表示8向右平移两位,结果为2.

    8的二进制1000,右移两位,0010.

现在想想,怎么样通过上面两个符号从一个int中得到某一个字节的数值,比如得到第二字节的值,也就是r的值。



假装你们想了20分钟。

我们用二进制来看,假设我们得到的color换算成二进制是:

01111100 01011010 10001101 00111110

我们要得到从左数第二个字节的值,怎么办?

首先,把这个数向右平移两个字节,也就是16位,那么就是color>>16,结果为

00000000 00000000 01111100 01011010

此时,再和11111111与一下,是不是就得到这个数本身了。

00000000 00000000 01111100 01011010

&

00000000 00000000 00000000 11111111

=

00000000 00000000 00000000 01011010

现在得到的就是r的值。写法:(color>>16)& 0xff.

0x表示16进制,16进制的ff表示的就是二进制的8个1。

以此类推,得到g就是(color>>8)& 0xff,得到b就是color & 0xff。

注:这里注意,方法不唯一,也可以先与后右移。这种方法是用位运算符来做的,当然也能转换成二进制数组然后拆分。那么为什么要用位运算符来做呢,第一是方便,第二就是很有逼格啊老铁。

如何让不同颜色匹配不同字符

int index = Math.round(gray * (base.length()) / 255);

gray是我们求出的灰度值,它的最大值也是255。不同颜色得到的gray不一样,同理,gray/255得到的就是不同颜色对应的一个比例,用这个比例乘以字符串长度就完成了不同颜色或者说不同颜色段匹配了不同的字符下标。因此得到gray * (base.length()) / 255,然后通过Math.round()方法四舍五入取整。

这里需要注意一下, 这样打印出来的字符图片色调会比较深,为了让颜色区分更明显,我们可以让一些接近于白色的浅色都打印成空格,那么我们就写成

int index = Math.round(gray * (base.length()+1) / 255);

把字符串长度加一,然后再做乘除操作,这样会让一部分颜色对应的索引大于字符串长度。

我们打印的时候是这样判断的,如果index大于等于字符串长度就打印空格,如果不是才打印字符。

为什么我的图片只能打一半

eclipse的控制台输出的行数有限,超过规定行数就覆盖了。这时可以把y++,改成y+=2,相当于每隔两个点取一下颜色并打印,也就是把原图纵向上缩小了一半。为了保持原图的比例,同理也可以把x做相应调整。

OutOfMemoryError错误

内存溢出,图片太大了,换个小一点的图片

为什么用汉字来打印图案会扭曲

字母或字符占得大小比汉字要小,汉字所占的空间很大,如果按照原有的点来打印汉字,因为汉字的宽度几乎比字母大一倍,所以会被挤出去,造成了扭曲。解决方法:改变y和x的自增数,也就是把x++改成+2,+3这样的,横向上每隔两个点打印一下,给汉字预留出空间

装逼时刻

找你女神的照片来打印,字符可以用“love”或者“我爱你”



放大



互联网+时代,表白当然也要用技术手段表白,程序员不是不解风情,我们的浪漫,与众不同。

Java字符拼成图片(image-ASCII)的更多相关文章

  1. python实战===老司机奇技淫巧系列之字符转换成图片

    先放两张效果图:

  2. Java PDF转换成图片并输出给前台展示

    首先需要导入所需工具类 <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>fo ...

  3. 将图片转为ASCII字符画

    原文:将图片转为ASCII字符画 Copyright 2012 Conmajia 源代码下载:点击这里 什么是字符画?就是用ASCII字符来近似组成图像,就像这样: ╭╮ ╭╮ ││ ││ ╭┴┴—— ...

  4. 【Java】字符拼接成字符串的注意点

    这两天敲代码的时候,偶然间发现一个好玩的事情,分享一下,记录一下. 该段代码主要是:先产生的几个整数,把整数转换成对应的字符,最后的字符拼接成字符串,在把字符拼接成字符串的时候,个人因为偷懒使用+号进 ...

  5. AsciiPic Java视频转成字符画

    AsciiPic Java视频转成字符画 github下载 https://github.com/dejavudwh/AsciiPic 运行截图 //没有做GUI 比较简陋 节省时间 main里的文件 ...

  6. Oracle一列的多行数据拼成一行显示字符

    Oracle一列的多行数据拼成一行显示字符   oracle 提供了两个函数WMSYS.WM_CONCAT 和 ListAgg函数.    www.2cto.com   先介绍:WMSYS.WM_CO ...

  7. java中如何把图片转换成二进制流的代码

    在学习期间,把开发过程经常用到的一些代码段做个备份,下边代码内容是关于java中如何把图片转换成二进制流的代码,应该能对各朋友也有用处. public byte[] SetImageToByteArr ...

  8. ditaa - 把ascii图形转成图片

    ditaa ditaa是一个把ascii图形转成图片的工具. 在查看zguide时看到这个文档是用gitdown生成的.zguide文档格式排版非常不错,以后要抽时间好好学习一下. 每章写一个txt文 ...

  9. 将java project打包成jar包,web project 打包成war包的几种演示 此博文包含图片

    转: http://blog.csdn.net/christine_ruan/article/details/7491559 http://developer.51cto.com/art/200907 ...

随机推荐

  1. 标准块CP功能实现

    #include<stdio.h> int main(int argc,char *argv[]) { FILE *src_fp,*des_fp; int src_ret; ]={}; ) ...

  2. Kafaka入门篇

    1.Apache Kafka - 简介 Apache Kafka是一个分布式发布 - 订阅消息系统和一个强大的队列,可以处理大量的数据,并使您能够将消息从一个端点传递到另一个端点. Kafka适合离线 ...

  3. how-to-fix-fs-re-evaluating-native-module-sources-is-not-supported-graceful

    http://stackoverflow.com/questions/37346512/how-to-fix-fs-re-evaluating-native-module-sources-is-not ...

  4. 为HttpClient和HttpURLConnection添加中国移动代理

    转自: http://www.2cto.com/kf/201111/112100.html 在android中,一般需要联网的时候前,都要做一次网络的判断,判断当前的网络状态!然后开始请求网络 当我们 ...

  5. Oracle的一些名词和概念

    1.数据库 这里的数据库不是通常情况下我们所说的数据库,而是一个Oracle的专业名词.它是磁盘上存储数据的集合,在物理上表现为数据文件. 日志文件和控制文件等,在逻辑上以表空间形式存在.使用时,必须 ...

  6. java基础(六):RabbitMQ 入门

    建议先了解为什么项目要使用 MQ 消息队列,MQ 消息队列有什么优点,如果在业务逻辑上没有此种需求,建议不要使用中间件.中间件对系统的性能做优化的同时,同时增加了系统的复杂性也维护难易度:其次,需要了 ...

  7. AJPFX关于StringBuffer类的总结

    StringBuffer类一.字符串缓冲区,是一个容器.没有子类不能继承.特点:长度可变化:可操作多个数据类型:可通过toString()变成字符串.二.存储方法1.StringBuffer appe ...

  8. 【数据分析 R语言实战】学习笔记 第三章 数据预处理 (下)

    3.3缺失值处理 R中缺失值以NA表示,判断数据是否存在缺失值的函数有两个,最基本的函数是is.na()它可以应用于向量.数据框等多种对象,返回逻辑值. > attach(data) The f ...

  9. 使用Jenkins进行android项目的自动构建(2)

    Maven and POM 1. 什么是Maven? 官方的解释是: http://maven.apache.org/guides/getting-started/index.html#What_is ...

  10. greenplum4.3.8.2安装

    GREENPLUM总体结构:   数据库由Master Severs和Segment Severs通过Interconnect互联组成. Master主机负责:建立与客户端的连接和管理:SQL的解析并 ...