java输出字母图形_Java字符拼成图⽚(image-ASCII)@
背景
⽤Java实现字符拼成⼀个图⽚,先看⼀下效果:
左边是原图,右边是⽤字符拼成的效果,即写好代码,读⼊⼀张图⽚,然后⽤指定的字符把这个图⽚的轮廓拼出来
放⼤之后是这个样⼦
⽤love拼成了哆啦A梦的轮廓
源码
public class AscPic {
public static void main(String[] args) throws IOException {
String path = "d:/qqq.png";//导⼊的图⽚
String base = "love";//将会⽤这个字符串⾥的字符填充图⽚
BufferedImage image = ad(new File(path));//读⼊图⽚,并⽤图⽚缓冲区对象来接收
//双层for循环,遍历图⽚
for (int y = 0; y < Height(); y++) {//先竖向遍历,再横向遍历,即⼀⾏⼀⾏的,后⾯也会⼀⾏⼀⾏的打印for (int x = 0; x < Width(); x++) {
int color = RGB(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 = und(gray * (base.length()) / 255);
if(index>=base.length()) {
System.out.print(" ");//⽩⾊的地⽅打空格,相当于⽩⾊背景,这样图⽚轮廓⽐较明显
}else {
System.out.print(base.charAt(index));//有颜⾊的地⽅打字符
}
}
System.out.println();//⼀⾏打完,换⾏
}
}
}
代码思路
整体思路为导⼊想好处理的图⽚,遍历,得到每个像素点的颜⾊,然后将其转换成灰度值(也就是把彩⾊转换成⿊⽩),根据得到的灰度值计算字符串索引,达到效果就是不同颜⾊可以对应不同的下标,以此来匹配字符串中的字符,最后按照原有的坐标点把这些字符打印出来即可
定义好想要填充的字符串
导⼊想要处理的图⽚,需要⽤到BufferedImage(图⽚缓冲区)这个对象
遍历整张图⽚,这⾥需要注意,外层循环遍历y轴,内层遍历x轴,因为打印的时候需要⼀⾏⼀⾏打印,打完⼀⾏要换⾏
根据getRGB(x,y)⽅法,传⼊当前的坐标点,得到当前点的颜⾊
从得到的颜⾊中单独拆分出r,g,b的值
根据得到的rgb计算对应的灰度值
根据灰度值计算索引
打印
难点讲解
如何得到rgb
int color = RGB(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中去单独得到从左数第⼆,第三,第四字节的数值。
ascii共有多少个字符怎么做呢?先来回顾两个位运算的基本知识:
"&",与,都是0则结果为0,都是1结果为1,⼀个1⼀个0结果为0.从数学上理解,&操作符其实是在取交集。
7&5=?
⾸先换算成⼆进制,7的⼆进制是0111,5的⼆进制是0101
0111&0101=0101,还是5。
通过上⾯的计算有没有发现⼀个规律,如果我想要让⼀个数和另⼀个数&完结果还是这个数本⾝怎么办?⽐如我想让0101,0111,0011和⼀个数&完结果还是他们本⾝,那么这个数应该是多少。
">>",右移.
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 = und(gray * (base.length()) / 255);
gray是我们求出的灰度值,它的最⼤值也是255。不同颜⾊得到的gray不⼀样,同理,gray/255得到的就是不同颜⾊对应的⼀个⽐例,⽤这个⽐例乘以字符串长度就完成了不同颜⾊或者说不同颜⾊段匹配了不同的字符下标。因此得到gray * (base.length()) / 255,然后通过und()⽅法四舍五⼊取整。
这⾥需要注意⼀下, 这样打印出来的字符图⽚⾊调会⽐较深,为了让颜⾊区分更明显,我们可以让⼀些接近于⽩⾊的浅⾊都打印成空格,那么我们就写成
int index = und(gray * (base.length()+1) / 255);
把字符串长度加⼀,然后再做乘除操作,这样会让⼀部分颜⾊对应的索引⼤于字符串长度。
我们打印的时候是这样判断的,如果index⼤于等于字符串长度就打印空格,如果不是才打印字符。
为什么我的图⽚只能打⼀半
eclipse的控制台输出的⾏数有限,超过规定⾏数就覆盖了。这时可以把y++,改成y+=2,相当于每隔
两个点取⼀下颜⾊并打印,也就是把原图纵向上缩⼩了⼀半。为了保持原图的⽐例,同理也可以把x做相应调整。
OutOfMemoryError错误
内存溢出,图⽚太⼤了,换个⼩⼀点的图⽚
为什么⽤汉字来打印图案会扭曲
字母或字符占得⼤⼩⽐汉字要⼩,汉字所占的空间很⼤,如果按照原有的点来打印汉字,因为汉字的宽度⼏乎⽐字母⼤⼀倍,所以会被挤出去,造成了扭曲。解决⽅法:改变y和x的⾃增数,也就是把x++改成+2,+3这样的,横向上每隔两个点打印⼀下,给汉字预留出空间
时刻
你⼥神的照⽚来打印,字符可以⽤“love”或者“我爱你”
放⼤

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。