SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

画像からASCIIアートを自動生成する

画像に最も近い文字を判定する方法


  • X ポスト
  • このエントリーをはてなブックマークに追加

余白を含めたフォントサイズを取得する

 文字画像を作成するためにも、まずは画像サイズを決めなければなりません。画像サイズは、余白も含めた文字サイズと同じにします。ここで考慮しなければならないのは、プロポーショナルフォントと等幅フォントの存在です。フォントには大きく分けて、文字によって横幅が異なるプロポーショナルフォントと、すべての文字の横幅が等しい等幅フォントとの2種類が存在します。近年のアスキーアートは某巨大掲示板に合わせてプロポーショナルフォントで作ることもありますが、本来は等幅フォントで作るものであり、またプログラムによる自動生成ではプロポーショナルフォントに対応するのは難しすぎるため、等幅フォント限定ということで話を進めます。

 フォントの詳細な情報は、GetTextMetrics関数に渡すと設定してくれるTEXTMETRIC構造体を調べれば分かります。LOGFONT構造体にもサイズを表すメンバが存在しますが、直接的にピクセル数を表しているとは限らないため使えません。

 ところでDIBSectionを作る方法ですが、私が作った画像入出力クラスBmpIOを使えば、BmpIO::CreateEmpty関数で空のDIBSectionを作ることができます。HDCはBmpIO::GetHDC関数で、ピクセル列を指すポインタはBmpIO::GetPixel関数で取得できます。他の公開関数については「BmpIO.txt」を見てください。

 以上をまとめると、文字を画像(DIBSection)に変換するためのプログラムは次のようになります。文字の色は黒、背景の色は白にします。これは後述する2値化した画像と比較するためです。

// 文字をビットマップに変換
static void AsciiToBmp(BmpIO *pBmpIO, int c, const LOGFONT *pLF)
{
    if(pBmpIO==NULL || pLF==NULL) return;

    HDC hdc=CreateDC("DISPLAY",NULL,NULL,NULL);  // デバイス作成

    HFONT hFont=CreateFontIndirect(pLF);         // フォント作成
    HFONT hOldFont=(HFONT)SelectObject(hdc,hFont);

    TEXTMETRIC tm;
    GetTextMetrics(hdc,&tm);           // フォント情報を取得

    SelectObject(hdc,hOldFont);
    DeleteDC(hdc);      // デバイス削除

    DWORD w=tm.tmAveCharWidth;         // 固定幅フォントの半角文字の幅
    DWORD h=tm.tmHeight;               // 文字の高さ
    pBmpIO->CreateEmpty(w,h,BIT24);    // ビットマップ作成

    hdc=pBmpIO->GetHDC();
    hOldFont=(HFONT)SelectObject(hdc,hFont);

    char str[2];
    wsprintf(str,"%c",c);
    TextOut(hdc,0,0,str,1);            // 文字をビットマップに描画

    SelectObject(hdc,hOldFont);
    DeleteObject(hFont);
}

文字画像と等しいサイズに画像の一部を切り取る

 アスキーアートに変換する原画像は文字画像より大きいため、文字画像と等しいサイズの小さな画像に分割しなければなりません。論理的に分割するだけなら原画像の参照範囲を設定するだけですが、より簡単に文字画像と比較するために、原画像の一部を切り取った分割画像を新たに作ります。

 プログラムは次のようになります。原画像の範囲外を参照した場合は文字画像の背景色である白に設定します。

// 一部を切り取ったビットマップを作成
static void SplitBmp(BmpIO *pDst, BmpIO *pSrc,
    int sx, int sy ,int dw, int dh)
{
    if(pDst==NULL || pSrc==NULL) return;

    // 作成するビットマップ
    pDst->CreateEmpty(dw,dh,BIT24);
    DWORD l=pDst->GetLength();
    BYTE *p=pDst->GetPixel();
    BYTE b=pDst->GetBitCount()/8;

    // 切り取り元ビットマップ
    int sw=(int)pSrc->GetWidth();
    int sh=(int)pSrc->GetHeight();
    DWORD sl=pSrc->GetLength();
    BYTE *sp=pSrc->GetPixel();

    if(p==NULL || sp==NULL) return;

    int x,y,xb,yl,sxb,syl;
    for(y=0;y<dh;y++)
    {
        yl=y*l; syl=(sy+y)*sl;
        for(x=0;x<dw;x++)
        {
            xb=x*b;
            if(sx+x>=0 && sx+x<sw && sy+y>=0 &&sy+y<sh)
            {
                sxb=(sx+x)*b;
                p[xb  +yl]=sp[sxb  +syl];
                p[xb+1+yl]=sp[sxb+1+syl];
                p[xb+2+yl]=sp[sxb+2+syl];
            }
            else        // 切り取り元ビットマップの範囲外
            {
                p[xb  +yl]=255;     // 白背景
                p[xb+1+yl]=255;
                p[xb+2+yl]=255;
            }
        }
    }
}

次のページ
切り取った画像に最も近い文字を選ぶ

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

ひよこ(ヒヨコ)

職業ゲームプログラマーを志す学生です。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/345 2006/05/26 15:42

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング