Bad apple for CSharp

前言:记得10年的时候我还在上学,有一天逛csdn看到了字符版的badapple,感受这东西好NB啊,而后就下载了一份,最近整理博客就把他整理博客,原做者是谁真心不知道,这是在果壳看到的.php

Bad Apple最初是做为日本同人游戏社团“上海爱莉丝幻乐团”所制做的“东方Project”系列游戏背景音乐出现的。后来在日本著名视频站点NicoVideo上有人萌发了为此系列制做MADMovie的想法,最后有了这段3D制做的影绘风格视频:编程

这段视频由不断变换的影绘风格游戏人物组成,彻底黑白,视觉震撼力十足。此视频在网络上引起了一场宏大的运动,各类技术宅使用了各类方法来演奏这段三分多钟长的视频。网络

看完本文后,你将能作出以下效果的视频:

 

 

1   所需工具

● Visual Studio 2010
● KMPlayer 3.0
 

2   思路原理

● 咱们想要作的就是在Windows控制台(也就是命令提示符,俗称“DOS窗口”)下播放Bad Apple视频,视频的基本原理就是利用人眼的视觉暂留效果,快速在控制台输出字符-清屏-输出字符-清屏,达到“动画”的效果。

而输出的字符须要从原始视频获取,将原始视频压缩成一个较低的分辨率,再获取到每一帧上的全部像素点,转换成对应的字符串。app

 

3   肯定思路

● 首先要选定使用的语言,这个根据你们的爱好,随便C/C++、Java、Python等等绝大部分编程语言均可以实现,这里选用的是C#。
● 关于素材的来源,这个是难度最大的步骤。原始1080P视频素材在网上很容易下到,但要处理成咱们须要的字符串形式。也就是在视频黑色像素的位置输出一个字符,白色像素的位置输出为空格,每一帧都依据像素排列位置输出成这种形式,如图:

 
● 获取字符图像大概有两个思路:
1. 根据视频格式(我下到的是H264编码方式的),读取二进制文件并进行转码;
2. 使用第三方工具或者类库,将视频的每一帧转换为图片格式,再将图片转换为所需字符串形式。
● 第一种方式须要研究视频编码的问题,并且比较复杂,所以咱们选择取巧的方式。KMPlayer是个很好的视频播放软件,它的截图功能很是强大,能够将视频每一帧图像都保存为截图,彻底可以知足咱们的须要。
 

4   捕获图像

● 使用KMPlayer打开原始视频,点击右键选择“捕获”->“高级捕获”(若是没有捕获项,请勾选“设置”->“高级菜单”项),就能够了。
● 须要考虑的是分辨率与帧数的问题,Windows7下1366×768分辨率默认命令提示符窗口最大一行显示80个字符,最大44列,所以咱们截取的视频分辨率应该小于等于这个值。固然还能够在后面过程当中在调整分辨率,但要设计插值计算的问题,所以仍是交给KMPlayer比较轻松一些。
● 另外命令提示符每一个字符的上下间距与左右间距是不一样的,上下间距要高上许多,若是按照视频原始宽高比截图的话最后输出的画面会有上下拉伸现象,所以截图的时候就要故意把视频截的“胖”一些。
● 最后综合考虑,决定一秒截25帧图像,图像分辨率为80×30。

 
● 这时还能够顺便将音频也用KMPlayer截取下来,以便保证视频与音频的同步(若是再如此折腾下尚未对此歌产生审美疲劳的话还能够丢进MP3里听)。音频比较简单,从头至尾录制,保存为mp3或者其余常见格式就能够,只要你使用的语言有可用的将它播放出来的库就能够。
 
● 截取的图片一共有3553张,保存在一个文件夹中。

● 其实这个时候就有一个简单的方法将这些静态图片播放出来了,只要使用Picasa附带的图片查看器打开第一帧图片,鼠标点中下一张,如何,动起来了吧?其余可以点种下一张不放就能够连续切换图片的查看器也能实现一样的效果。
 

5   编码

● 下面就能够开始编码了,思路很容易,依次读取每一张图片,遍历图片上每个像素点,若是是黑色的话输出一个字符(这里用#字符,由于#块头比较大而且用的是C#),白色的话输出一个空格,将字符串保存在一个文本文件里。
● 仔细观察保存的图片时会发现一个问题,Bad Apple视频里并非绝对的黑色与白色,还有少许的灰色做为渐变色彩,所以判断黑白的时候仍是根据RGB值,只要其中一项大于200便看成黑色处理就行了。
● 第一次保存的时候发现数据量比较大,未压缩大概有将近10M大小,有些难以接受。由于保存的文本大部分字符都是重复的,所以压缩空间应该很是大,因而使用.net自带的Gzip压缩了一下,果真文件大小降至517KB了。
● 此时还能够用另一种方法播放这段“文本视频”,只要用记事本打开数据文件,调整记事本窗口的宽度与高度(宽度大于一行80个字符,高度恰好每行30个字符),而后按下PageDown按钮,看到效果了吧~!
 

6   输出

● 剩下的工做就比较简单,只要读取这个文件,并在控制台不停的输出,每次输出30行(由于横向分辨率为30),而后清屏,继续输出。只要设定好每次输出时间的间隔,与截屏的间隔相符的话,动画就能够顺利的在控制台播放出来了。
● 只要仔细调节截屏的帧数、分辨率,控制台的宽度与高度、输出时间的间隔,就必定能够顺利播放出来,固然这个过程比较繁琐。
● 最后给程序添加上背景音乐,这里使用第三方的 NAudio库来播放咱们一开始截取的音频文件,只要你以前调节的参数没有问题,音频应该和动画达到神同步的境界的。让咱们随着Bad Apple的旋律哼唱着“多少红颜为傻逼 多少傻逼不珍惜”来运行咱们的程序吧!

 

7   源码预览

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //获取in目录下全部文件 未做异常处理
 6             string[] path = Directory.GetFileSystemEntries("in");
 7             
 8             //保存为Gzip压缩的文本流
 9             FileStream fileStream = new FileStream("badapple.dat", FileMode.Create, FileAccess.Write);
10             GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Compress);
11             StreamWriter sw = new StreamWriter(compressionStream);
12 
13             //遍历每一个像素点
14             for (int x=0;x<path.Length;x++)
15             {
16                 //Console.WriteLine(path[x]);
17                 Bitmap bitmap = new Bitmap(path[x]);
18 
19                 for (int i = 0; i < bitmap.Height; i++)
20                 {
21                     for (int j = 0; j < bitmap.Width; j++)
22                     {
23                         Color color = bitmap.GetPixel(j, i);
24                         if (color.R > 200)
25                         {
26                             //Console.Write("1");
27                             sw.Write(" ");
28                         }
29                         else
30                         {
31                             //Console.Write("0");
32                             sw.Write("#");
33                         }
34                     }
35                     //Console.WriteLine();
36                     sw.WriteLine();
37                     Console.Clear();
38                     Console.WriteLine("{0} of {1} completed! ",x,path.Length);
39                 }
40                 bitmap.Dispose();
41             }
42             sw.Dispose();
43         }
44        
45     }
压缩图片
 1    class Program
 2     {
 3         private static StreamReader reader;
 4         static void Main(string[] args)
 5         {
 6             //打开压缩文本流
 7             FileStream fileStream = new FileStream("badapple.dat", FileMode.Open, FileAccess.Read);
 8             GZipStream compressionStream = new GZipStream(fileStream, CompressionMode.Decompress);
 9             reader = new StreamReader(compressionStream);
10 
11             //初始化控制台参数
12             Console.Title = "BadAppleSharp 0.1";
13             Console.WindowWidth = 80;
14             Console.WindowHeight = 31;
15 
16             //调用播放器播放背景音乐
17             WaveStream mp3Reader = new Mp3FileReader(@"BadApple.mp3");
18             WaveStream pcmStreamer = WaveFormatConversionStream.CreatePcmStream(mp3Reader);
19             WaveStream blockAlignedStream = new BlockAlignReductionStream(pcmStreamer);
20             WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
21             waveOut.Init(blockAlignedStream);
22             waveOut.Play();
23 
24             //初始化定时器
25             Timer timer = new Timer(55);
26             timer.Elapsed+=new ElapsedEventHandler(Render);
27             timer.Enabled = true;
28 
29             Console.ReadKey();
30             waveOut.Dispose();
31             blockAlignedStream.Dispose();
32             pcmStreamer.Dispose();
33             mp3Reader.Dispose();
34         }
35 
36         //渲染一帧
37         public static void Render(Object sender,ElapsedEventArgs e)
38         {
39             {
40                 Console.Clear();
41                 StringBuilder data=new StringBuilder(2500);
42                 for (int i = 0; i < 30; i++)
43                 {
44                     data.Append(reader.ReadLine());
45                 }
46                 Console.Write(data.ToString());
47             }
48 
49         }
50     }
播放源码

8   源码下载

程序及源代码下载 (须要本机安装.net2.0或以上运行环境)编程语言

转载自果壳艾斯昆ide

相关文章
相关标签/搜索