用 Java 生成 ASCII 字元畫(2)

ImportNew發表於2015-05-18

上一篇部落格裡我用Java建立了一個簡單的Ascii 字元畫生成器(可以從GitHub上獲取), 文章釋出之後我收到了很多反饋。所以今天我打算繼續在這個專案上新增一些新特性,期待能受到更多歡迎。我重新設計了核心部分,目的是增加擴充套件性以便測試不同的演算法以及產生多樣化的結果。 在本文中,我會展示本專案的全新架構,方便您整合進自己的專案中以及根據需要進行擴充套件。

架構

用Java生成字元畫(2)

AsciiImgCache

在任何ascii字元渲染髮生前,我們需要建立一個此類的例項。 它需要一個字型和字元列表作為引數,然後它將為每個字元生成一個圖片的Map。如果你嫌麻煩,也有預設的字元列表提你選擇。

提供給對此感興趣的讀者:

private static final char[] defaultCharacters = 
    "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft///|()1{}[]?-_+~<>i!lI;:,/"^`'. "

示例

// use only '/' '/' and ' '
AsciiImgCache mediumBlackAndWhiteCache = AsciiImgCache.
    create(new Font("Courier", Font.BOLD, 10), new char[] {'//', ' ', '/'});

// use default list
AsciiImgCache largeFontCache = AsciiImgCache.
    create(new Font("Courier",Font.PLAIN, 16));

BestCharacterFitStrategy

這是一個演算法的抽象,用來判斷源影像與每個字元的相似度。該抽象只有一個方法:

float calculateError(final GrayscaleMatrix character, final GrayscaleMatrix tile);

對於具體的實現來說,它應該比較兩個圖片並返回浮點格式的偏差值。每個字元都會被用來進行比較,最終採用返回最小偏差值的字元。目前有兩種實現:

ColorSquareErrorFitStrategy

這個實現非常容易理解。 它比較每個畫素然後計算灰度差異的均方誤差(Mean squared error)。

數學表示式如下:

用Java生成字元畫(2)

其中n代表畫素的數量, C和T分別代表字元和tile影像的畫素。

StructuralSimilarityFitStrategy

結構相似性(SSIM)索引演算法聲稱能還原人類視角,其目標是提升傳統的諸如MSE的演算法。此處我不打算詳細解釋其原理,如果你感興趣可以在Wikipedia上閱讀相關資料。我自己也進行了一些嘗試並實現了一個版本,似乎能在我們的用例中獲得不錯的結果。

AsciiConverter

這是整個流程的核心,它包含源影像取樣(tiling)的邏輯,呼叫具體實現計算出最匹配的字元。然而,它並不知道如何建立ascii 字元 – 這需要子類來實現。 目前有兩個實現:AsciiToImageConverter and AsciiToStringConverter – 顧名思義,它們分別輸出影像和字串。

示例用法:

正所謂埋頭苦幹勝於紙上談兵,下面就整合各個部分,給大家展示整個流程:

// initialize cache
AsciiImgCache cache = AsciiImgCache.create(new Font("Courier",Font.BOLD, 6));

// load image
BufferedImage portraitImage = ImageIO.read(new File("image.png"));

// initialize converters
AsciiToImageConverter imageConverter = 
    new AsciiToImageConverter(cache, new ColorSquareErrorFitStrategy());
AsciiToStringConverter stringConverter = 
    new AsciiToStringConverter(cache, new StructuralSimilarityFitStrategy());

// image output
ImageIO.write(imageConverter.convertImage(portraitImage), "png", 
    new File("ascii_art.png"));
// string converter, output to console
System.out.println(stringConverter.convertImage(portraitImage));

下面還有一些通過設定不同引數產生的圖片:

原始圖片

16 pts字型,MSE
16 pts字型, SSIM
10 pts字型3字元,MSE
10 pts字型3字元,SSIM
6 pts字型,MSE
6 pts字型,SSIM

下一步工作

下面是對未來工作的一些想法:

  • 研究並實現更多的影像比較演算法。
  • 對圖片重新處理達到更佳效果(改進對比度,使用邊緣監測等)。
  • 通過並行處理改進影像處理效果,根據結果評估是否值得改進。
  • 增加一些轉換格式(比如html輸出)。
  • 支援輸出帶顏色的字元。
  • 為專案新增測試。

對程式碼有建議或者發現任何問題,歡迎通過GitHub評論和提交!

相關文章