之前在B站经常看到有up制作badapple的字符串动画,感觉挺好玩的,今天自己也尝试了一下。

制作字符串动画其实原理也挺简单,

1. 首先,将视频保存为一帧一帧的图片。

2. 然后,用程序读取图片的每个像素点,并获取像素点的颜色值,判断颜色接近黑色还是接近白色,接近黑色的写入字符串到txt文件,接近白色的写入一个空格。

3. 逐张的读取转换好的txt文件显示出来就是逐帧动画了。

下面就以bad apple这段视频作为素材吧,黑白色简单点。

一、准备材料

1. bad apple的视频(自行百度)

2. 绘声绘影,用来将视频转成图片

二、视频转成图片

打开绘声绘影,在下面的时间轴右键,插入视频

!["QQ截图20180325164403.png"/]("{#ZC_BLOG_HOST#}zb_users/upload/2018/03/201803251521967741499394.png" ""QQ截图20180325164403.png"")然后点击右上角的\[共享\],格式选择自定义-友立图像序列,点右边的齿轮设置一下输出的像素,设置小一点,不需要多大的像素,多了转成字符串也显示不了那么多。

!["QQ截图20180325165028.png"/]("{#ZC_BLOG_HOST#}zb_users/upload/2018/03/201803251521968100623278.png" ""QQ截图20180325165028.png"")

点击开始,稍等一会视频就转换完成了。

二、将图片转成字符串

附上java代码

package com.test;

import java.awt.image.BufferedImage;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.OutputStreamWriter;

import java.util.Random;

import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

public class Image {

public static void main(String\[\] args) {

//初始化线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(100, Integer.MAX\_VALUE, 200, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>());

//一共要转换5477张图片

for (int i = 0; i < 5477; i++) {

MyTask myTask = new MyTask(i);

executor.execute(myTask);

}

executor.shutdown();

}

}

/\*\*

 \* 处理线程

 \* @author Administrator

 \*

 \*/

class MyTask implements Runnable {

private int num;

public MyTask(int num) {

this.num = num;

}

@Override

public void run() {

try {

System.out.println("正在转换第 " + num + "张图片......");

StringBuilder imageName=new StringBuilder("T");

for (int i = 0; i < 4 - String.valueOf(num).length(); i++) {

imageName.append("0");

}

imageName.append(num).append(".JPG");

StringBuilder imgPath=new StringBuilder("C:/Users/Administrator/Desktop/badapple/");

imgPath.append(imageName.toString());

StringBuilder txtPath=new StringBuilder("F:\badappletxt").append(num).append(".txt");

image2txt(imgPath.toString(), txtPath.toString());

System.out.println("第 " + num + "张图片转换完毕");

} catch (Exception e) {

e.printStackTrace();

}

}

/\*\*

 \* 将图片转换为字符串

 \* @param imgPath

 \* @param txtPath

 \* @throws Exception

 \*/

public void image2txt(String imgPath, String txtPath) throws Exception {

int\[\] rgb = new int\[3\];

File file = new File(imgPath);

BufferedImage bi = null;

try {

bi = ImageIO.read(file);

} catch (Exception e) {

e.printStackTrace();

}

int width = bi.getWidth();

int height = bi.getHeight();

int minx = bi.getMinX();

int miny = bi.getMinY();

StringBuilder sb=new StringBuilder();

for (int i = miny; i < height; i++) {

for (int j = minx; j < width; j++) {

int pixel = bi.getRGB(j, i); // 下面三行代码将一个数字转换为RGB数字

rgb\[0\] = (pixel & 0xff0000) >> 16;

rgb\[1\] = (pixel & 0xff00) >> 8;

rgb\[2\] = (pixel & 0xff);

//判断接近白色还是黑色,接近白色写入空格,接近黑色写入字符串

if (rgb\[0\] > 175&&rgb\[1\]>175&&rgb\[2\]>175) {

sb.append(" ");

} else {

sb.append(getRandomString(1));

}

}

sb.append("

");

}

writeToTxt(txtPath, sb.toString());

}

/\*\*

 \* 将字符串写入文件

 \* @param filePath

 \* @param content

 \*/

public void writeToTxt(String filePath, String content) {

BufferedWriter out = null;

try {

out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath, true)));

out.write(content);

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

out.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

/\*\*

 \* 获取随机字符串

 \* @param length

 \* @return

 \*/

public  String getRandomString(int length) {

// 定义一个字符串(A-Z,a-z,0-9)即62位;

String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";

// 由Random生成随机数

Random random = new Random();

StringBuffer sb = new StringBuffer();

// 长度为几就循环几次

for (int i = 0; i < length; ++i) {

// 产生0-61的数字

int number = random.nextInt(62);

// 将产生的数字通过length次承载到sb中

sb.append(str.charAt(number));

}

// 将承载的字符转换成字符串

return sb.toString();

}

}

判断颜色的时候我看了下调色板发现rgb都大于175基本上就是白色了,因为bad apple只有黑白两个色,理论上来说要不都是255,要不都是0,但是刚才转图片的时候改了分辨率,产生了颜色损失,可能边缘的地方不都是255或者0,索性就判断一下吧。bad apple还挺准的,其他视频没试过不知道,应该不怎么准。

三、显示字符串

将图片转换成字符串文本之后,将文本一个一个的读取然后显示出来就可以动起来了。可以用java做个桌面程序显示,或者直接控制台也可以。用js也可以做,用ajax请求txt文件然后把文本输出到canvas上也行。这部分就不做了,没别的,就是懒![]("{#ZC_BLOG_HOST#}zb_users/emotion/default/evilgrin.png"/)