Конструируем AutoDiver v1.0

AutoDiver v1.0

Сверхзадача автоматизации формулируется просто: всё, что может быть автоматизировано — должно быть автоматизировано (и рано или поздно — будет). На странный вопрос, который почему-то часто рано или поздно понурым упрёком всплывает в подобном контексте: "возможно ли автоматизировать творчество?", следует сразу ответить "а мы и не пытаемся".

Автоматизация касается лишь рутинных составляющих процесса, на которые в результате тратится на порядок меньше времени и, как следствие, творцу становится легче удержать в памяти искру озарения и запечатлеть её сияние.

Итак, какова одна из самых рутинных составляющих процесса рисования на ZX Spectrum?

(Впрочем, уверен, что, возможно, кто-то может относиться к ней, как одной из самых увлекательных и творческих частей: решение нетривиальной логической задачи, приносящее удовольствие.)

Я бы назвал самым рутинным процесс первоначальной подгонки цветного наброска под атрибутную сетку спектрума. И поручил бы решение этой задачи компьютеру.

Итак у нас есть цветной набросок 256x192 цвет-на-точку (совершенно случайно в палитре спектрума 8/15 цветов), нам нужно научить компьютер "подгонять" изображение под сетку (это задача №1), а потом определять хорош ли данный вариант подгонки или нет (это задача №2).

Задача №1

Как живой Diver подгоняет набросок под атрибутную сетку?
По наитию.

А как это делает Autodiver?
  1. Двигает картинку вверх-вниз и вправо-влево.
  2. Масштабирует картинку (увеличивает-уменьшает) в каких-то пределах.
  3. Вращает картинку.

Для первой операции существует всего 8*8 = 64 варианта сдвига. (Если сдвинуть на 8 вниз или влево — смещение внутри атрибутной сетки будет таким же как исходное состояние картинки.)
Для второй операции имеет смысл масштабирование в пределах от 100% до 200%.
Вращение имеет смысл от -45 до +45 градусов (большие значения угла поворота — дадут нам повторы относительного расположения внутри сетки).

Отлично, в самом дотошном случае комбинация подобных преобразований даст нам 64 * 256 * 90 = 1474560 вариантов.

Не так уж и много.

Но на практике нам понадобится ещё меньше по следующим причинам: вращение исходника на -45 и +45 градусов не всегда приемлемо и на самом деле достаточно поворотов от -15 до +15, низкое разрешение также позволяет повысить шаг изменения угла поворота: для получения различимых результатов достаточно двух-трёх градусов.

Масштабирование в 2 раза также не всегда приемлемо и достаточно перебрать масштабы от 100% до 150% с таким же шагом 2-4 пикселя.

64 * 32 * 10 = 20480 вариантов.

Задача №2

И вот есть у нас двадцать тысяч картинок.
Как живой Diver выбирает какой из них лучше? Каковы критерии оценки?

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

Таким образом метрика очевидна: сумма потенциальных потерь в каждом знакоместе и будет искомой мерой.

В каждом знакоместе картинки мы считаем количество пикселей каждого цвета, сортируем цвета по убыванию распространённости и подсчитываем количество пикселей с третьего места по частоте: их мы потеряем при накладывании ограничений.
Затем суммируем "потери" и получаем рейтинг картинки.

Чем он меньше — тем меньше потерь, тем мы ближе к идеалу.

N.B. Кажется, что улучшить эту схему можно введя ещё одну сущность: маску значимости пикселей (по сути — это монохромная картинка с разрешением аналогичным исходной картинке, в которой пиксели соответствующие значащим из первой картинки — белые, а соответствующие незначащим из первой картинки — чёрные). С маской можно игнорировать потерю "незначащих" пикселей. Но на самом деле проще избавиться от незначащих пикселей непосредственно на исходной картинке.

Практика

Генерация

Самым простым и мощным инструментом для пакетного преобразования картинок практически в любых исходных форматах является ImageMagick — он есть для всех основных платформ.

Описываемое выше преобразование с помощью ImageMagick будет выглядеть так:

convert source_image.png -gravity center -filter point -resize #{newsize} -rotate #{rotateangle} +repage -crop 256x192#{offsetx}#{offsety} -gravity forget -extent 256x192 target_image.png

где:

  • #{new_size} — это новый размер
  • #{rotate_angle} — угол поворота
  • #{offsetx}#{offsety} — смещения по x и y

Например:
convert in.png -gravity center -filter point -resize 257x -rotate 8 +repage -crop 256x192+4+4 -gravity forget -extent 256x192+4+4 ./eval/in.pngr8s257x4y4.png

Для генерации 20 тысяч таких строк мы воспользуемся соответствующим генератором на ruby "gen.rb":

ruby gen.rb in.png 30 3 128 4 1 0

где:

  1. in.png — исходная картинка
  2. 30 — угол отклонения между первым и последним вариантом (то есть от -15 до +15)
  3. 3 — шаг угла вращения
  4. 128 — максимальный добавочный размер (256+128 = 384)
  5. 4 — шаг масштабирования
  6. 1 — необходимость перебора всех 64 смещений
  7. 0 — отсутствие маски (над маской, которая должна носить имя mask{sourcefile} — то есть в нашем случае maskin.png проделываются все те же операции вращения, смещения и масштабирования что и над исходной картинкой с тем лишь отличием, что маска преобразуется в монохром и складывается в каталог ./evalmask, а не ./eval, в дальнейшем маска используется при оценке).

Результатом работы генератора является командный .sh или .bat файл, который следует запустить для начала массовых преобразований над картинкой.

После выполнения массовых преобразований исходные картинки будут в каталоге ./eval (от evaluate) и ./eval_mask (если маска использовалась).

Оценка

Оценка выполняется с помощью файла ev.rb:

ruby ev.rb

Результатом его работы являются файлы: result.csv (с результатами оценки всех картинок) и best.csv (c результатами, отсортированными по рейтингу) в каталоге ./best.
А также в каталог ./best копируется несколько лучших картинок (с лучшим рейтингом).

Резюме


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

Исходники выложены тут: github.com/oisee/autodiver
Fork'айте, дорабатывайте, переписывайте на своих любимых языках программирования (первую часть «генератор» — можно легко переписать на .bat/.sh)

Спасибо за внимание =)

UPD1:

FAQ
Q:Не убъёт ли autodiver GFX-сцену?
A:Нет, autodiver исключительный пацифист, также как autosiril, который за 4 года существования никого не только не убил, но и не оживил.

Q:Не является ли название autodiver — оскорбительным для Diver'а?
Отвечает Diver/4D:
A:Нет не является абсолютно. Мне даже польстило. Вообще то автодайвера мы придумали вместе с сирилом. Просто программер он, а не я. Поэтому он и написал его. {отя у меня есть прототип, который писал я сам в bat.

Q:Не является ли название autosiril — оскорбительным для Siril'а?
Отвечает Taylor S.:
A: shake, shake, shake, shake, shake.

Q:Как пользоваться этим вашим конвертором?
A:Autodiver, как и Diver против конверсий, а потому он ничего не конвертирует. Autodiver просто просматривает тысячи вариантов преобразованного изображения и рекомендует лучший. Тот, который потеряет наименьшее количество пикселей при накладывании ограничений атрибутной сетки.

UPD2

Q:Примеры!
A:Ок:

ИсходникВариант, выбранный autodiverbmp2scr

UPD3

diver4d: Идея набрасывать в цвете на точку. Это просто технология быстрого получения цветного наброска в скр с наименьшим клэшингом. Для чистых конверсий это неприменимо. Точнее применимо, но там слишком много других параметров, которые автодайвер не применяет. То есть конверсия/срисовка в спек цвет — в 8 или 15 цветов предполагается, что уже сделана. Для этого надо или с нуля нарисовать скетч, либо как минимум обвести оригинал по второму слою, самостоятельно выбирая цвета и границы теней.

30 комментариев

avatar
гӱт! надо будет тоже на практике попробовать.
avatar
Best1
avatar
Сорвалось. В общем, это «лучшие варианты» найденные для различных картинок.
Вот ещё парочка:
avatar
avatar
мдя… уже есть автосирил (подозреваю, есть и автоммсм :)), уже есть запилятор, теперь есть автодайвер :) надо будет повтыкать…
avatar
автоmmcm делается из автоsiril'а изменением файла module_template.rb =)
avatar
oisee , пожалуйста, расскажи подробнее о «Оценке» картинок
  • VBI
  • +3
avatar
Вся оценка умещается в следующую функцию:
на вход которой поступает img — картинка, px и py — координаты знакоместа (в пикселях):

def eval_cell(img,px,py)
cell = img.get_pixels(px,py,8,8) # получаем массив пикселей 8x8 (64) по коодинатам px и py

rcell = cell.reduce(Hash.new(0)) {|a,b| a[b] += 1;a} #из массива конструируем словарь/хэш у которого ключ — это цвет пикселя(сам пиксель), а значение — количество этих пикселей в массиве

acell = rcell.values.sort.reverse[2,64] #получаем массив пикселей, ниже второго места по распространённости в нашем знакоместе — «потерянные» пиксели
bcell = acell != nil? acell: [0] # если таких не было (в знакоместе только один или два цвета) — то создаём массив из одного элемента, равный нулю.
err = bcell.inject(0, &:+) # суммируем элементы массива — получаем сумму «потерянных» пикселей в знакоместе
end

то есть, предположим у нас есть массив:

1,1,1,1,1,1,1,1
1,1,1,1,1,1,1,1
1,1,1,1,0,0,0,0
1,1,1,1,0,0,0,0
1,1,1,1,0,0,0,0
1,1,1,1,2,2,2,3

То на первом этапе получим хэш 1=>44, 0=>16, 2=>3, 3=>1
затем получим все, что ниже «второго места»: 3, 1
затем суммируем: 4.
«Штрафной рейтинг» знакоместа = 4.

Повторить 768 раз =)
avatar
То есть наш AutoDiver (как и настоящий!) ничего не конвертирует =) Только приценивается и выбирает лучшее =)
avatar
В случае с маской добавляется дополнительный фильтр: сначала смотрим, есть ли в текущем знакоместе маски 2 разных цвета (ч/б), или всё белое: если да — то знакоместо значащее и его оценка сохраняется. А если в знакоместе маски только чёрный цвет — то знакоместо незначащее и оценка у него 0.
avatar
Маска полной значимости или полной незначимости, на практике получается бесполезна: проще удалить незначащие пиксели непосредственно на исходнике.

А вот битовая (монохромная) маска повышенной значимости — поможет штрафовать сильнее за утерю пикселей повышенной значимости. Ну то есть если обмазать такой маской рот или глаза — штраф за их утерю будет выше. =)
avatar
Что ещё можно сказать: так как первая задача решается с помощью командной строки: работа её происходит медленнее, если бы эти же самые преобразования происходили с помощью библиотеки и прямо в памяти и не было бы открытия исходника каждый раз заново — всё работало бы быстрее. После преобразования картинки можно было бы сразу выполнить оценку — и не сохранять целиком всё изображение, а только параметры преобразования и оценку.
avatar
Одна надежда на Vitamin =) только он может сделать быстро и понятно на c/cpp, и чтобы на всех платформах бинарник работал =)
avatar
Где скачать автодайвера? НАДО!
  • AAA
  • 0
avatar
Autodiver v1.0 — вон там в самом начале публикации написано =)

Но тут важно понимать требования для запуска:
1) поставить ImageMagick,
2) поставить ruby,
3) поставить RMagick (для этого нужно [a] поставить DevKit)
avatar
Так что лучше подождать готовых бинарников =)
avatar
Я делаю так. Беруц исходнубю картинку и кладу на огромный квадрат черного цвета. В пнг. загружаю в юмп2сцр и руками двигаю квадрат с посередине лежащей картннкой, подгоняю атрибуты руками на глаз, потом сохраняю и дорисовываю края и в паинте дорисовываю несовпадения. В трудных случаях сетку кладу и квадраты 8 на 8 просчитываю, подгоняю, уменьшаю или увеличиваю каждое знакоместо чтоюы попало в цвета.
  • AAA
  • +1
avatar
Идея скрипта для генерации 64 вариантов (сдвигов) конверсии через BMP2SCR возникла у меня из-за того, что в BMP2SCR нет попиксельного сдвига с клавиатуры, есть только произвольный сдвиг мышкой (удивите меня, если я ошибаюсь).

Оценивать расхождение с оригиналом я пытался по размеру файла с xor-разницей, сохраняемой в оптимизированный PNG. Но в результате оценку произвожу на глаз, ориентируясь по наиболее важным элементам картинки. Фактически в BMP2SCR происходит то же самое, но наобум. Я лишь снизил для себя вероятность потери оптимальных вариантов.
avatar
А если делать то же самое познакоместно?
avatar
Что конкретно сделать познакоместно?
avatar
При срисовывании мы подгоняем оригинал под аттрибутную сетку, но не весь за раз, а кусками. Я вот задумался:
1. Можно ли распознавать формы на оригинале и пододвигать их отдельно, не всей картинкой?
2. Если нет, то, может быть, тупо каждое знакоместо двигать отдельно? Результат на больших картинках может быть любопытным.
avatar
Да, одно из направлений развития, это (полу)автоматическое или ручное выделение объектов в слои, которые можно подгонять раздельно, задав лишь условие неперекрываемости.

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

Все эти идеи обсуждались в процессе мозгоштурма нового идеального графического редактора: оказалось что простой интнрактивной многослойности со слоями «цвет на точку» при моментальном перерендеринге в спекрум-атрибуты при любом изменении — достаточно для значительного упрощения рисования и снятия рутины.
avatar
То есть интерактивный инструмент может быть таким:

Неограниченное количество слоёв со спектрум-палитрой в режиме «цвет на точку».
Каждый слой может независимо от остальных смещаться, двигаться и т.п. — редактироваться.
У каждого слоя есть «вес» при слиянии и рендеринге в спектрум режим: область просмотра которого всегда видна и доступна, любые изменения в любом из слоёв сразу отражаются там.
avatar
Эффект с познакоместным сдвигом может выглядеть как прикольный эффект. Но уточни пожалуйста какой именно сдвиг ты имеешь ввиду?
avatar
Я думал о чем-то таком: для каждого знакоместа пройтись, и считать из оригинала разные варианты с небольшим смещением (+-2 пикселя, например). Нужно придумать, как оценивать, какой из вариантов брать — как вариант, стараться скомбинировать минимальное количество цветов в результате. В таком случае геометрические фигуры нарушатся
Еще можно учитывать смещение конкретных знакомест для коррекции приоритета смещения в соседних знакоместах. Типа, если мы верхнее знакоместо взяли со сдвигом на два пикселя вниз, то нижнее знакоместо с бОльшей вероятностью (при прочих равных? по какой-то количественной оценке?) должно выбрать смещение в ту же сторону.
avatar
То есть, берем знакоместо по координатам 16:32.
Пытаемся считать из оригинала блок 8*8 по координатам в 14:30, 15:30, 16:30, 17:31, 18:31… 18:34.
Для каждого блока подсчитываем количество цветов и выбираем блок с наименьшим количеством/наиболее выгодно сочетаемыми цветами.
Для следующего знакоместа частично (!) учитываем уже рассчитанное направление смещения в соседних.
avatar
Правильно ли я понял что:
1) мы идём «в обратную сторону», то есть пляшем от спектрумовской атрибутной сетки.
2) выбираем стартовое знакоместо и подгоняем, двигая (всю?) картинку под него на +-2 пикселя (что даёт нам 5 вариантов по горизонтали и 5 по вертикали, итого: 25 вариантов).
3) переходим к следующему соседнему знакоместу и также подгоняем двигая всю картинку (25 вариантов).
5) перебираем так всю атрибутную сетку: для каждого знакоместа у нас есть массив лучших вариантов, отсортированных по убыванию.

6) самый главный этап: пытаемся для каждого знакоместа выбрать лучший вариант с учётом лучших вариантов для соседних знакомест (тут количество переборов растёт по экспоненте).
avatar
В принципе да. Во втором пункте мысль правильная, но можно не двигать всю картинку, а сразу читать нужный кусок по нужным координатам.
Да, получается 25 вариантов на каждое знакоместо — больше, наверное, смысла нет делать, будет сильно портиться геометрия оригинала.

Для такого метода, возможно, потребовалось бы все функции писать самому — отдельно сварганить такое через bmp2scr гипотетически возможно, но сложно.
avatar
Мне кажется, что это прокатит только для очень крупных картинок, где и так хорошо все конвертируется. Для мелких деталей, особенно лиц, сдвиг на пиксель — смерти подобен.
avatar
Предложенный мной велосипед метод 100% не универсален. С ним самая большая беда, что, пока не реализуешь, результат непредсказуем, а на реализацию надо потратить время всё же.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.