Изменить размер растеризованного текста и сделать его непиксельным


11

Это скриншот текста, набранного в текстовом редакторе:

Текст высотой 16px

Это тот же текст в большем размере.

Текст высотой 96 пикселей

Обратите внимание на то, насколько заметен псевдоним на буквенных диагональных штрихах, таких как xи z. Эта проблема является основной причиной, по которой растровые шрифты потеряли популярность для таких «масштабируемых» форматов, как TrueType.

Но, возможно, это не проблема, связанная с растровыми шрифтами, просто с тем, как обычно осуществляется их масштабирование. Вот альтернативный рендеринг с использованием простой билинейной интерполяции в сочетании с пороговым определением .

Текст высотой 96 пикселей с билинейной интерполяцией

Это гладко, но не идеально. Диагональные штрихи по - прежнему неровные, и изогнутые буквы , как cи oдо сих пор многоугольники. Это особенно заметно при больших размерах.

Так есть ли лучший способ?

Задание

Напишите программу, которая принимает три аргумента командной строки.

resize INPUT_FILE OUTPUT_FILE SCALE_FACTOR

где

  • INPUT_FILE - это имя входного файла, который считается файлом изображения, содержащим черный текст на белом фоне. Вы можете использовать любой основной формат растровых изображений (PNG, BMP и т. Д.), Который вам удобен.
  • OUTPUT_FILE - имя выходного файла. Это может быть формат растрового или векторного изображения. Вы можете ввести цвет, если выполняете субпиксельный рендеринг в стиле ClearType.
  • SCALE_FACTOR - это положительное значение с плавающей точкой, которое указывает, насколько может быть изменено изображение. При заданном входном файле x × y px и коэффициенте масштабирования s выходные данные будут иметь размер sx × sy px (с округлением до целых чисел).

Вы можете использовать стороннюю библиотеку обработки изображений с открытым исходным кодом.

В дополнение к вашему коду включите примеры выходных данных вашей программы в масштабах 1.333, 1.5, 2, 3 и 4, используя мое первое изображение в качестве входных данных. Вы также можете попробовать его с другими шрифтами, в том числе пропорционально разнесенными.

счет

Это конкурс популярности. Запись с наибольшим количеством голосов минус побед победит. В случае точной ничьей побеждает более ранняя заявка.

Изменить : Срок продлен из-за отсутствия записей. TBA.

Избирателям рекомендуется судить, основываясь главным образом на том, насколько хорошо выглядят выходные изображения, а во-вторых, на простоте / элегантности алгоритма.


SCALE_FACTORВсегда ли > 1?
kennytm

@kennytm: Да. Отредактировал, чтобы явно перечислить факторы масштаба.
dan04

Можем ли мы предположить, что на изображении только одна строка текста?
GiantTree

@GiantTree: Да. Вы можете поддерживать многострочный текст, если хотите, но это не обязательно.
dan04

Ответы:


4

Рубин, с 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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.