@

背景

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

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



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

源码

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

代码思路

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

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

难点讲解

如何得到rgb

  1. int color = image.getRGB(x, y);//图片缓冲区自带的方法,可以得到当前点的颜色值,返回值是int类型
  2. int r=(color>>16)&0xff;
  3. int g=(color>>8)&0xff;
  4. 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。

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

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

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

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

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

  1. 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. 《windows核心编程系列》十六谈谈内存映射文件

    内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非系统的页交换文件.将文件映射到内存中后,我们就可以在内存中操作他们了 ...

  2. break跳出嵌套循环体

    package com.wh.Object; public class Test { public static void main(String[] args) { // TODO Auto-gen ...

  3. hdu 6011 Lotus and Characters 贪心

    http://acm.hdu.edu.cn/showproblem.php?pid=6011 先把数字从小到大排好,比如是-6.3.4这样, 然后处理出后缀和,当后缀和 <= 0的时候马上停止就 ...

  4. Lucky Number Eight dp

    https://www.hackerrank.com/contests/w28/challenges/lucky-number-eight 设dp[i][v]表示前i位数中,得到余数是v的子序列的数目 ...

  5. Codeforces Beta Round #98 (Div. 2)(A-E)

    A #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> ...

  6. SQL Server 编程入门

    一.T—SQL 的组成 1.DML(数据操作语言 Data Manipulation Language) 查询.插入.删除和修改数据库中的数据.SELECT.INSERT.UPDATE.DELETE ...

  7. AngularJS入门 & 分页 & CRUD示例

    一.AngularJS 简介 ​ AngularJS  诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中. ...

  8. Nginx反向代理node,实现让静态文件在同一域

    Nginx反向代理node,实现让静态文件在同一域 原文https://github.com/zhuangZhou/Blog/issues/4 不管是Vue还是React,还是传统的网站,与node服 ...

  9. InChatter系统之服务客户端的开发

    今天终于开始客户端的开发了,客户端完成以后,我们将可以进行简单的交流.开发完成的程序只是一个很简单的雏形,在本系统完成以后,以及完成的过程中,大家都可以下载源码,在此基础上融入自己的想法和尝试,可以按 ...

  10. SELECT TOP 100 PERCENT * 的含义

    --返回符合条件的100%的记录,即所有符合条件的记录SELECT TOP 100 PERCENT * --返回符合条件的100条记录,即只返回符合条件的100条记录SELECT TOP 100 * ...