Edited at
qnoteDay 14

【アスキーアート職人歓喜?】画像からアスキーアートを生成

More than 3 years have passed since last update.

どっかで聞いたことあるタイトルですが、前回書いた「【ドット絵職人歓喜】アスキーアートを画像に変換」のやつとは別のライブラリです。

今回は画像からアスキーアートを生成するライブラリです。

こんな感じ。

ポルナレフ.png

ありのまま起こったことを話すぜ!

ということで BKAsciiImage というライブラリを使用してアスキーアートを生成してみましょうというお話です。


BKAsciiImage

BKAsciiImage は GitHub にて公開されている iOS 向けのライブラリです。

■ BKAsciiImage

https://github.com/bkoc/BKAsciiImage

CocoaPods 対応なので、下記一行を Podfile に書いてインストール。

pod "BKAsciiImage"

ちょろっと試したい人は try オプションでサンプルプロジェクトを開いてみましょう。

pod try BKAsciiImage

この try オプションはプロジェクトを作らなくてもテンポラリなプロジェクトを作成してサンプルを実行できるので便利です。


使い方

使い方ですが、説明が不要なくらい簡単なインタフェースです。

// インスタンスを作って

BKAsciiConverter *converter = [[BKAsciiConverter alloc] init];
// コンバート
[converter convertImage:image completionHandler:^(UIImage *asciiImage) {
// 非同期で取得
self.imageView.image = asciiImage;
}];

あれ?って思った方は感が鋭い。そう、生成したアスキーアートはアスキー文字列、つまり NSString ではなく、UIImage で返されます。逆にすごいですね。もちろん文字列で返すこともできます。

[_converter convertToString:image completionHandler:^(NSString *asciiString) {

NSLog(@"%@", asciiString);
}];

サンプルにもありますが、アスキーアートの文字に色を付けるかどうか(grayscale)、文字の大きさ、色の反転、などをプロパティで指定できます。

converter.grayscale = YES; // グレースケール

converter.font = [UIFont systemFontOfSize:4]; // フォント
converter.reversedLuminance = NO; // 反転


試してみた

手持ちの写真とかいろいろ試してみました。


写真1

まずは手持ちの写真から。弊社猫社員の営業部長です。

部長

ちょっとよくわかんないですね。

拡大するとこんな感じ。一応アスキーアートしてますね。

部長拡大


写真2

写真の陰影に結構左右されるような感じですかね。少し明るい写真で試してみた。猫社員達の父親「たび助」です。

たび助

お、なかなかよい感じ。


モノクロ画像

ってことはモノクロ画像から出力すると綺麗に出力できる?

某2chで有名なAAを再現。

スクリーンショット 2015-12-14 1.09.42.png

ダレ(Dare)

(生1904年5月11日 - 没1989年1月23日 スペイン)


イラストから生成

最後はイラストから生成。これはうまくいきました。(ペコッターのペコちゃんお借りしました。すみませんすみません)

スクリーンショット 2015-12-14 7.23.20.png


一応まじめに中を見る

アスキーアートに変換する他のライブラリとか他のエンジンを使っているのかと思ったら意外と単純な処理でした。ピクセル単位で画素を抜き出し、RGB取って、近似値の文字に変換するような感じです。以下、BKAsciiDefinition.m から抜粋。


BKAsciiDefinition.m

    CGFloat blockWidth = input.size.width / pixelGrid.width;

CGFloat blockHeight = input.size.height / pixelGrid.height;

for (int x=0; x < pixelGrid.width; x++) {
for (int y=0; y < pixelGrid.height; y++) {

int col = x; int row = y;

block_t block = [pixelGrid blockAtRow:row col:col];

CGFloat luminance = [self _luminance:block];

NSString *asciiResult = [asciiDefinition stringForLuminance:luminance];
CGRect rect = CGRectMake(blockWidth * col, blockHeight * row, blockWidth, blockHeight);

if (!grayscale)
CGContextSetFillColorWithColor(ctx, [UIColor colorWithRed:block.r green:block.g blue:block.b alpha:1.0].CGColor);
else
CGContextSetGrayFillColor(ctx, luminance, 1.0);

const char *cString = [asciiResult UTF8String];
CGContextShowTextAtPoint(ctx, rect.origin.x, rect.origin.y, cString, strlen(cString));
}
}


ここの block_t というのが RGBA の値を持つ構造体です。定義はそのまんま。

typedef struct block {

CGFloat r;
CGFloat g;
CGFloat b;
CGFloat a;
} block_t;

その後の stringForLuminance というメソッドで予め定義されたアスキー文字に近いものに変換している模様。ちなみに、予め定義されたアスキー文字列はこんな感じ。


BKAsciiDefinition.m

NSDictionary *dictionary = @{  @1.0: @" ",

@0.95:@"`",
@0.92:@".",
@0.9 :@",",
@0.8 :@"-",
@0.75:@"~",
@0.7 :@"+",
@0.65:@"<",
@0.6 :@">",
@0.55:@"o",
@0.5 :@"=",
@0.35:@"*",
@0.3 :@"%",
@0.1 :@"X",
@0.0 :@"@"
};

Dictionaryのキーと block_t の値を比較して一番近いアスキー文字を返しているようです。意外と単純!


まとめ

タイトル煽り気味ですが実際アスキーアート職人さんは歓喜どころか鼻で笑うかもしれません。内部的には他の生成エンジンを使っているわけではないので、カスタマイズ次第でより精度の高いものが仕上がるかもですね。

というわけで、お試しあれ!