Ubah ukuran teks yang dirasterisasi dan membuatnya terlihat non-pixeled

11

Ini adalah tangkapan layar dari beberapa teks yang diketik dalam editor teks:

Teks setinggi 16px

Ini adalah teks yang sama dengan ukuran yang lebih besar.

Teks setinggi 96px

Perhatikan betapa terlihatnya aliasing pada huruf dengan goresan diagonal yang menonjol seperti xdan z. Masalah ini adalah alasan utama mengapa font raster telah kehilangan popularitas karena format "scalable" seperti TrueType.

Tapi mungkin ini bukan masalah inheren dengan font raster, hanya dengan cara penskalaannya yang biasanya diterapkan. Berikut ini adalah render alternatif menggunakan interpolasi bilinear sederhana dikombinasikan dengan ambang batas .

Teks setinggi 96px diterjemahkan dengan interpolasi bilinear

Ini lebih halus, tetapi tidak ideal. Sapuan diagonal masih bergelombang, dan huruf melengkung suka cdan omasih poligon. Ini terutama terlihat pada ukuran besar.

Jadi adakah cara yang lebih baik?

Tugas

Tulis program yang membutuhkan tiga argumen baris perintah.

resize INPUT_FILE OUTPUT_FILE SCALE_FACTOR

dimana

  • INPUT_FILE adalah nama file input, yang diasumsikan sebagai file gambar yang berisi teks hitam pada latar belakang putih. Anda dapat menggunakan format gambar raster utama (PNG, BMP, dll.) Yang sesuai.
  • OUTPUT_FILE adalah nama file output. Ini bisa berupa format gambar raster atau vektor. Anda dapat memperkenalkan warna jika Anda melakukan rendering subpixel seperti ClearType.
  • SCALE_FACTOR adalah nilai titik-mengambang positif yang menunjukkan seberapa besar ukuran gambar dapat diubah. Diberikan file input x × y px dan faktor penskalaan s , output akan memiliki ukuran sx × sy px (dibulatkan menjadi bilangan bulat).

Anda dapat menggunakan pustaka pemrosesan gambar sumber terbuka pary ketiga.

Selain kode Anda, sertakan contoh output program Anda dengan faktor skala 1,333, 1,5, 2, 3, dan 4 menggunakan gambar pertama saya sebagai input. Anda juga dapat mencobanya dengan font lain, termasuk yang spasi proporsional.

Mencetak gol

Ini adalah kontes popularitas. Entri dengan jumlah upvote terbesar dikurangi dengan downvotes menang. Dalam kasus dasi yang tepat, entri sebelumnya menang.

Sunting : Batas waktu diperpanjang karena kurangnya entri. TBA.

Pemilih didorong untuk menilai terutama berdasarkan seberapa baik gambar output terlihat, dan yang kedua pada kesederhanaan / keanggunan algoritma.

dan04
sumber
Apakah SCALE_FACTORselalu> 1?
kennytm
@kennytm: Ya. Telah diedit untuk secara eksplisit mencantumkan faktor skala.
dan04
Bisakah kita berasumsi hanya ada satu baris teks dalam gambar?
GiantTree
@GiantTree: Ya. Anda dapat mendukung teks multi-baris jika Anda mau, tetapi ini tidak wajib.
dan04

Jawaban:

4

Ruby, dengan RMagick

Algoritma ini sangat sederhana — temukan pola piksel yang terlihat seperti ini:

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

dan tambahkan segitiga untuk membuat mereka terlihat seperti ini:

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

Kode:

#!/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')

Keluaran (klik apa saja untuk melihat gambar dengan sendirinya):

1.333

1.333

1.5

1.5

2

2

3

3

4

4

Gagang pintu
sumber