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

相關文章
相關標籤/搜索