Java字符拼成图片(image

Wesley13
• 阅读 637

@TOC

背景

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

  • 左边是原图,右边是用字符拼成的效果,即写好代码,读入一张图片,然后用指定的字符把这个图片的轮廓拼出来 Java字符拼成图片(image 放大之后是这个样子
  • 用love拼成了哆啦A梦的轮廓 Java字符拼成图片(image

源码

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的值。

Java字符拼成图片(image 假装你们想了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 放大 Java字符拼成图片(image 互联网+时代,表白当然也要用技术手段表白,程序员不是不解风情,我们的浪漫,与众不同。

点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
4个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
技术小男生 技术小男生
4个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi/etc/profile2:按字母键i进入编辑模式,在最底部添加内容:JAVAHOME/opt/jdk1.8.0152CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jarPATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
4个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
4个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小森森 小森森
4个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
Peter20 Peter20
1年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Karen110 Karen110
1年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
艾木酱 艾木酱
3个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Stella981 Stella981
1年前
OpenCV检测轮廓极点(Python C++)
    今天分享一个OpenCV检测轮廓极点实例,原图如下,我们需要检测出地图中最大轮廓的上下左右四个极点,并进行标注显示。!(https://oscimg.oschina.net/oscnet/ae374a72c5404b00b0e976e499eedf36.png)    第一步:阈值处理分割出地图轮廓!(ht
helloworld_28799839 helloworld_28799839
4个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue