ラスタライズされたテキストのサイズを変更し、ピクセル化されていないように見せます


11

これは、テキストエディターで入力したテキストのスクリーンショットです。

16pxの高さのテキスト

これは、より大きなサイズの同じテキストです。

96pxの高さのテキスト

およびのような顕著な対角線のある文字で、エイリアシングがどれほど見えるかに注目してください。この問題は、ラスターフォントがTrueTypeのような「スケーラブル」フォーマットへの人気を失った主な理由です。xz

しかし、これはラスターフォントに固有の問題ではなく、それらのスケーリングが通常実装される方法だけである可能性があります。以下に、しきい値処理と組み合わせた単純な双線形補間を使用した代替レンダリングを示します。

バイリニア補間でレンダリングされた96ピクセルの高さのテキスト

これはよりスムーズですが、理想的ではありません。斜めのストロークはまだでこぼこであり、湾曲した文字は好きco、まだポリゴンです。これは特に大きなサイズで顕著です。

それでは、もっと良い方法はありますか?

タスク

3つのコマンドライン引数を取るプログラムを作成します。

resize INPUT_FILE OUTPUT_FILE SCALE_FACTOR

どこ

  • INPUT_FILEは入力ファイルの名前です。これは、白い背景に黒いテキストを含む画像ファイルであると想定されています。便利なメインストリームラスターイメージ形式(PNG、BMPなど)を使用できます。
  • OUTPUT_FILEは、出力ファイルの名前です。ラスター形式またはベクター画像形式のいずれかです。ClearTypeのようなサブピクセルレンダリングを実行している場合、色を導入できます。
  • SCALE_FACTORは、画像のサイズを変更できる正の浮動小数点値です。所与のX × Yピクセルの入力ファイルとを因子スケールSは、出力の大きさを有することになるSX × SY PX(整数に丸められます)。

サードパーティのオープンソース画像処理ライブラリを使用できます。

コードに加えて、最初の画像を入力として使用してスケールファクター1.333、1.5、2、3、および4でプログラムのサンプル出力を含めます。比例間隔のフォントなど、他のフォントで試すこともできます。

得点

これは人気コンテストです。投票数から投票数を引いたものが最も多いエントリが勝ちです。正確に同点の場合、以前のエントリが優先されます。

編集:エントリの不足により期限が延長されました。未定。

投票者は、主に出力画像の見栄えに基づいて判断し、次にアルゴリズムのシンプルさ/優雅さに基づいて判断することをお勧めします。


SCALE_FACTOR常に> 1?
ケニー

@kennytm:はい。スケール係数を明示的にリストするように編集しました。
dan04

画像にテキストが1行しかないと仮定できますか?
ジャイアントツリー

@GiantTree:はい。必要に応じて複数行のテキストをサポートできますが、これは必須ではありません。
dan04

回答:


4

Ruby、RMagickを使用

アルゴリズムは非常に単純です。次のようなピクセルのパターンを見つけます。

    ####
    ####
    ####
    ####
########
########
########
########

三角形を追加して、次のようにします。

    ####
   #####
  ######
 #######
########
########
########
########

コード:

#!/usr/bin/ruby

require 'rmagick'
require 'rvg/rvg'
include Magick

img = Image.read(ARGV[0] || 'img.png').first
pixels = []
img.each_pixel{|px, x, y|
    if px.red == 0 && px.green == 0 && px.blue == 0
        pixels.push [x, y]
    end
}

scale = ARGV[2].to_f || 5.0
rvg = RVG.new((img.columns * scale).to_i, (img.rows * scale).to_i)
    .viewbox(0, 0, img.columns, img.rows) {|cnv|
    # draw all regular pixels
    pixels.each do |p|
        cnv.rect(1, 1, p[0], p[1])
    end
    # now collect all 2x2 rectangles of pixels
    getpx = ->x, y { !!pixels.find{|p| p[0] == x && p[1] == y } }
    rects = [*0..img.columns].product([*0..img.rows]).map{|x, y|
        [[x, y], [
            [getpx[x, y  ], getpx[x+1, y  ]],
            [getpx[x, y+1], getpx[x+1, y+1]]
        ]]
    }
    # WARNING: ugly code repetition ahead
    # (TODO: ... fix that)
    # find this pattern:
    # ?X
    # XO
    # where X = black pixel, O = white pixel, ? = anything
    rects.select{|r| r[1][0][1] && r[1][1][0] && !r[1][2][1] }
        .each do |r|
            x, y = r[0]
            cnv.polygon x+1,y+1, x+2,y+1, x+1,y+2
        end
    # OX
    # X?
    rects.select{|r| r[1][0][1] && r[1][3][0] && !r[1][0][0] }
        .each do |r|
            x, y = r[0]
            cnv.polygon x+1,y+1, x+0,y+1, x+1,y+0
        end
    # X?
    # OX
    rects.select{|r| r[1][0][0] && r[1][4][1] && !r[1][5][0] }
        .each do |r|
            x, y = r[0]
            cnv.polygon x+1,y+1, x+0,y+1, x+1,y+2
        end
    # XO
    # ?X
    rects.select{|r| r[1][0][0] && r[1][6][1] && !r[1][0][1] }
        .each do |r|
            x, y = r[0]
            cnv.polygon x+1,y+1, x+2,y+1, x+1,y+0
        end
}
rvg.draw.write(ARGV[1] || 'out.png')

出力(いずれかをクリックすると、画像が表示されます):

1.333

1.333

1.5

1.5

2

2

3

3

4

4

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.