Ändern Sie die Größe von gerastertem Text und lassen Sie ihn nicht pixelig aussehen

11

Dies ist ein Screenshot von Text, der in einem Texteditor eingegeben wurde:

16px hoher Text

Dies ist der gleiche Text in einer größeren Größe.

96px hoher Text

Beachten Sie, wie sichtbar das Aliasing bei Buchstaben mit markanten diagonalen Strichen wie xund ist z. Dieses Problem ist ein Hauptgrund dafür, dass Raster-Schriftarten durch „skalierbare“ Formate wie TrueType an Popularität verloren haben.

Aber vielleicht ist dies kein inhärentes Problem bei Raster-Schriftarten, nur bei der Art und Weise, wie die Skalierung normalerweise implementiert wird. Hier ist ein alternatives Rendering mit einfacher bilinearer Interpolation in Kombination mit Schwellenwerten .

96px hoher Text mit bilinearer Interpolation gerendert

Dies ist reibungsloser, aber nicht ideal. Diagonale Striche sind immer noch holprig und gekrümmte Buchstaben mögen cund osind immer noch Polygone. Dies macht sich insbesondere bei großen Größen bemerkbar.

Gibt es einen besseren Weg?

Die Aufgabe

Schreiben Sie ein Programm, das drei Befehlszeilenargumente akzeptiert.

resize INPUT_FILE OUTPUT_FILE SCALE_FACTOR

wo

  • INPUT_FILE ist der Name der Eingabedatei, bei der es sich um eine Bilddatei handelt, die schwarzen Text auf weißem Hintergrund enthält. Sie können jedes gängige Rasterbildformat (PNG, BMP usw.) verwenden, das praktisch ist.
  • OUTPUT_FILE ist der Name der Ausgabedatei. Es kann sich entweder um ein Raster- oder ein Vektorbildformat handeln. Sie können Farbe einführen, wenn Sie ein ClearType-ähnliches Subpixel-Rendering ausführen.
  • SCALE_FACTOR ist ein positiver Gleitkommawert, der angibt, um wie viel die Bildgröße geändert werden kann. Bei einer x × y px-Eingabedatei und einem Skalierungsfaktor s hat die Ausgabe eine Größe von sx × sy px (auf ganze Zahlen gerundet).

Sie können eine Open-Source-Bildverarbeitungsbibliothek von Third Pary verwenden.

Fügen Sie zusätzlich zu Ihrem Code Beispielausgaben Ihres Programms mit Skalierungsfaktoren von 1,333, 1,5, 2, 3 und 4 hinzu, wobei Sie mein erstes Bild als Eingabe verwenden. Sie können es auch mit anderen Schriftarten versuchen, einschließlich Schriftarten mit proportionalem Abstand.

Wertung

Dies ist ein Beliebtheitswettbewerb. Der Eintrag mit der größten Anzahl von Upvotes minus Downvotes gewinnt. Bei einem genauen Gleichstand gewinnt der frühere Eintrag.

Bearbeiten : Frist wegen fehlender Einträge verlängert. TBA.

Die Wähler werden aufgefordert, in erster Linie anhand des Aussehens der Ausgabebilder und in zweiter Linie anhand der Einfachheit / Eleganz des Algorithmus zu beurteilen.

dan04
quelle
Ist SCALE_FACTORimmer> 1?
Kennytm
@ Kennethytm: Ja. Haben bearbeitet, um die Skalierungsfaktoren explizit aufzulisten.
dan04
Können wir annehmen, dass das Bild nur eine Textzeile enthält?
GiantTree
@ GiantTree: Ja. Sie können mehrzeiligen Text unterstützen, wenn Sie möchten, dies ist jedoch nicht erforderlich.
dan04

Antworten:

4

Ruby mit RMagick

Der Algorithmus ist sehr einfach: Finden Sie Muster von Pixeln, die so aussehen:

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

und füge Dreiecke hinzu, damit sie so aussehen:

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

Code:

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

Ausgaben (klicken Sie auf eine beliebige Stelle, um das Bild selbst anzuzeigen):

1,333

1,333

1.5

1.5

2

2

3

3

4

4

Türknauf
quelle