PS 刚开始开发写的代码,觉得挺有意思的,只需要更改你本地文字或图片路径即可运行有代码问题可友好进行讨论
题目内容
一个字节有 8 位。
UTF-8 码位有 1-4 个字节。
字节可以被视为从左到右的位流模式。
可以包含多个字节的比特流,因此 UTF-8 代码 点也可以被视为从左到右的位流模式。
UTF-8 字符串具有多个 UTF-8 码位,因此 UTF-8 字符串也可以被视为从左到右的位流模式。
可以将比特流模式可视化为单声道图片,每个 0 作为白色像素,每个 1 作为黑色像素。因此,64 字节 字符串可以可视化为 256 像素(2561 或 648 或其他) 一致的分辨率)单声道图片。
我们将这种单声道图片命名为“比特” 图’。
图片转文字
package week05.day23.perfectImageAndWords;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
//把图片解析成01代码,再根据utf-8规则去解析文件
import javax.imageio.ImageIO;
public class TestPic {
//解析图片并按照utf-8编码转为文字
public static void main(String[] args) throws Exception {
//1、将图片转为二进制文件存储--图片路径
int[][] mtr = readPic("e:/image.png");//请改成你计算机上图片位置
//2、把二维数组转换成一维数组,不然处理的时候可能上一行未完成,解析不到下一行
int[] bits = new int[mtr.length*mtr[0].length];
// System.out.println(bits.length);
int index = 0;
for (int i = 0; i < mtr.length; i++) {//这段代码是查看解析的二维数组是否正确
for(int j=0;j<mtr[i].length;j++) {
bits[index++]=mtr[i][j];
}
}
// System.out.println(bits.length);
//按照utf-8的编码规范去解析二进制文件
// int[] bi = {1,1,1,0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,1,1,0};
// System.out.println(bi.length);
parseUTF(bits);
}
/*
* utf-8的编码规则
* 一字节:0XXXXXXX
* 二字节:110XXXXX 10XXXXXX
* 三字节:1110XXXX 10XXXXXX 10XXXXXX
* 四字节:11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
*/
private static void parseUTF(int[] bits) {
int index=0;//这个指针指向每一行循环时列下标的位置,下一行开始时重新置位0
int count=0;//这个变量记录每个UTF-8字符的字节数,至少1字节
for (int j = 0; j < bits.length; ) {
//循环每一行数组数据-从头去读--先读这个字符是几个字节组成的
//然后按utf-8的编码格式组合字符串,转换为字符即可
if(bits[j]==1) {
count++;
index++;
}else {
index++;//这一位是1后面跟的那一个0
//不为1,则说明已经解析完字节数,count里面存的如果是0表示是单字节数据
//如果不是0则表示是多字节数据
String str="";//定义一个字符串,记录这个字符的二进制字符串
if(count==0) {
//单字节处理--往后读7位即可
for(int k=1;k<=7;k++) {
str += bits[index++];//每一个字符位获取后行内指向列的指针++
}
}else {
//多个字节--这里要分为两部分来处理,第一部分处理第一字节
//第一字节数据的个数是8-count-1,因为1后面要跟一个0
for(int x=0;x<8-(count+1);x++) {
str+=bits[index++];
}
//还剩count-1个字节的数据需要获取,这些数据都是6个长
for(int y=0;y<count-1;y++) {
index+=2;//跳过不需要处理的10
for(int z=0;z<6;z++) {
str+=bits[index++];
}
}
}
//处理完后count归0,进行下一字符的解析
count=0;
// System.out.println(str);
System.out.print((char)Integer.parseInt(str, 2));
str="";
}
j=index;
}
}
//这个方法负责把图片的黑白像素转换为01二进制,1为黑,0为白
private static int[][] readPic(String path) throws Exception {
BufferedImage image = ImageIO.read(new FileInputStream(new File(path)));
int w = image.getWidth();
int h = image.getHeight();
// System.out.println(w+" "+h+"宽高");
int[][] mtr = new int[h][w]; // 存储矩阵
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int rgb = image.getRGB(j, i); // aRGB
// System.out.println(rgb);
// 依次检查R、G、B是否超过阈值
// 超过视为白色,否则黑色
int r = (rgb & 0x00ff0000) >> 16;
int g = (rgb & 0x0000ff00) >> 8;
int b = (rgb & 0x000000ff);
Color color = new Color(rgb);
// System.out.print(color.getRed()+"\t");
// System.out.print(color.getGreen()+"\t");
// System.out.print(color.getBlue()+"\t");
// System.out.println(color.getAlpha()+"\t");
// System.out.println(r+"\t"+g+"\t"+b);
int a = 0xff / 2; // 阈值, 可根据需要设定
if (r > a && g > a && b > a) {
mtr[i][j] = 0;//题目要求0作为白色,1作为黑色
} else {
mtr[i][j] = 1;
}
}
}
return mtr;
}
}
文字转图片
package week05.day23.perfectImageAndWords;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import javax.imageio.ImageIO;
//把文字根据utf-8解析成01代码,再根据01代码转为图片
public class TestWord {
// BufferedImage image = ImageIO.write(new , formatName, output);
public static void main(String[] args) throws Exception {
//需要在e:/word.txt创建一个文件存储至少16字节以上的文字信息,因为宽度定位最少128位
// 二进制01代码字符串
// String str = "01010101010101011110000100001111000001000111100000100001011100010101010101010101";
String path = "e:/word.txt";
String str = readWords(path);
// System.out.println(str);
parseImage(str);//二进制转为图片方法
}
private static String readWords(String path) throws Exception {
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(path),"utf-8"));
char[] cuf = new char[80];
int length = -1;
String str = "";//存储从文件中读取到的字符串
String endStr="";//存储最终字符串 编制后的01码
//检查str里是否将文本内容读取完
while((length=br.read(cuf))!=-1) {
str+=new String(cuf,0,length);
}
System.out.println(str);
for (int i = 0; i < str.length(); i++) {
byte[] bit=Character.toString(str.charAt(i)).getBytes("utf-8");//单个字符
if(bit.length==1) {
//单字节的解析前面直接补0
//得到单字符的长度在前面补0存起来,然后截取后八位
String temp = "00000000"+Integer.toBinaryString(bit[0]);
//截取它的后八位
endStr +=temp.substring(temp.length()-8);
}else {
// System.out.println(endStr);
for(int j=0;j<bit.length;j++) {
//遍历每一个字符的字节转成二进制截取后八位然后重新拼成字符串
endStr += Integer.toBinaryString(bit[j]).substring(24);
}
}
}
System.out.println(endStr);
return endStr;
}
private static void parseImage(String code) throws Exception {
int[] pixels = new int[code.length()];
for (int i = 0; i < code.length(); i++) {
//由字符串转成整数0、1方便后续乘以颜色值进行上色 下面则是进行颜色控制
int value = code.charAt(i) - '0';
if(value==0){
pixels[i]=-1;//根据之前的testPic输出的rgb值得出的白色
}else {
pixels[i]=-16777216;//黑色
}
}
int width = code.length()/128;//比如总长100,除以33,width得出为3 128可以更改,设定为一行128像素
int height = code.length()/width+1;//若不加1像素高度,h为33,丢失了一个位置的存储信息
int index = 0;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int i=0;i<height;i++)//像素点坐标,横向画入
for(int j=0;j<width;j++) {
//像素起始点,终止点。以及16进制颜色转成十进制数
if(index<pixels.length-1) {//控制index,保证不超限
img.setRGB(j, i, pixels[index]);
index++;
}else{//超限的像素部分直接画黑色进行填充
img.setRGB(j, i, 0x00000000);
}
}
File file = new File("e:/image.png");
ImageIO.write(img, "png", file);
System.out.println("创建成功");
}
}