怎么样用Java实现将一张图片转成字符画??
输入:一张图片
输出:一个txt文档,由字符组成,看起来很像那张照片的字符画。
基本思想:凭据照片的灰度接纳差别的字符画出来,形成一个灰度表。
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Test {
public static void main(String[] args) throws IOException {
String path = "D:/logo.jpg";//导入的图片
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();//一行打完,换行
}
}
}
代码思绪
整体思绪为导入想好处置的图片,遍历,获得每个像素点的颜色,然后将其转换成灰度值(也就是把彩色转换成是非),凭据获得的灰度值盘算字符串索引,到达效果就是差别颜色可以对应差别的下标,以此来匹配字符串中的字符,最后根据原有的坐标点把这些字符打印出来即可
界说好想要填充的字符串
导入想要处置的图片,需要用到BufferedImage(图片缓冲区)这个工具
遍历整张图片,这里需要注重,外层循环遍历y轴,内层遍历x轴,由于打印的时刻需要一行一行打印,打完一行要换行
凭据getRGB(x,y)方式,传入当前的坐标点,获得当前点的颜色
从获得的颜色中单独拆分出r,g,b的值
凭据获得的rgb盘算对应的灰度值
凭据灰度值盘算索引
打印
难点解说
若何获得RGB
int color = image.getRGB(x, y);//图片缓冲区自带的方式,可以获得当前点的颜色值,返回值是int类型
int r=(color>>16)&0xff;
int g=(color>>8)&0xff;
int b=color&0xff;
1
2
3
4
首先明确一点,所谓的rgb就是三原色,red,green,blue,无论是ps照样程序,都市通过给rgb赋值来拼成一个新的颜色。通过etRGB(x,y)获得的颜色是一个int类型,我们用color来示意。这个值包罗四部分内容,分别为a,r,g,b,即透明度,red,green,blue。每一部分正好占一个字节。以是我们要做的就是从这个int中去单独获得从左数第二,第三,第四字节的数值。
怎么做呢?先来回首两个位运算的基本知识:
“&”,与,都是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 = Math.round(gray * (base.length()) / 255);
1
gray是我们求出的灰度值,它的最大值也是255。差别颜色获得的gray不一样,同理,gray/255获得的就是差别颜色对应的一个比例,用这个比例乘以字符串长度就完成了差别颜色或者说差别颜色段匹配了差别的字符下标。因此获得gray * (base.length()) / 255,然后通过Math.round()方式四舍五入取整。
这里需要注重一下, 这样打印出来的字符图片色调会对照深,为了让颜色区分更显著,我们可以让一些接近于白色的浅色都打印成空格,那么我们就写成
int index = Math.round(gray * (base.length()+1) / 255);
1
把字符串长度加一,然后再做乘除操作,这样会让一部分颜色对应的索引大于字符串长度。
我们打印的时刻是这样判断的,若是index大于即是字符串长度就打印空格,若是不是才打印字符。
为什么我的图片只能打一半
eclipse的控制台输出的行数有限,跨越划定行数就覆盖了。这时可以把y++,改成y+=2,相当于每隔两个点取一下颜色并打印,也就是把原图纵向上缩小了一半。为了保持原图的比例,同理也可以把x做响应调整。
OUTOFMEMORYERROR错误
内存溢出,图片太大了,换个小一点的图片
为什么用汉字来打印图案会扭曲
字母或字符占得巨细比汉字要小,汉字所占的空间很大,若是根据原有的点来打印汉字,由于汉字的宽度险些比字母大一倍,以是会被挤出去,造成了扭曲。解决方式:改变y和x的自增数,也就是把x++改成+2,+3这样的,横向上每隔两个点打印一下,给汉字预留出空间
原文链接:https://www.cnblogs.com/wangshuaiandroid/p/12863393.html
本站声明:网站内容来源于网络,若有侵权,请联系我们,我们将及时处置。