Создание изображения из произвольных строк

Я видел много изображений для ascii арт-преобразователей, но то, что мне нужно, немного сложнее. Вместо мозаичного изображения, созданного из символов ascii, мне нужно иметь возможность использовать предопределенный набор строк произвольной длины в качестве мозаичных плит.

Я немного поработал над Googling, но я даже не уверен, как структурировать мой запрос? Есть ли что-нибудь подобное? И бонус, если он не будет использовать дубликаты в генерации изображений.

Ответ 1

Не подходит для Stack Overflow, но интересный проект. Поэтому я немного поучаствовал и посмотрел, как далеко я могу добраться.

Я думаю, это сводится к следующему:

  • вычислить серое значение для набора символов ASCII;
  • вычислить "наилучшее соответствие" для каждой строки;
  • повторить 2. до завершения.

Я не думаю, что "без дубликатов" возможно, если у вас нет очень маленьких изображений и много строк-кандидатов. Это то, что я придумал, используя мой аватар и мои текущие значки в виде списка строк. (Я исключил свой значок "c", так или иначе, моя программа решила, что это "наилучшая подгонка" для больших патчей, что было не очень привлекательно. Ментальная заметка: не включать строки с 1 символом.)

QuorumQuorumAutobiographerQuorumQuorumQuorumJongwareQuorumQuorumQuorumQuorumQuor
umQuorumQuorumAutobiographerQuorumQuorumSupporterQuorumQuorumProofreaderQuorumQu
orumQuorumAutobiographerQuorumQuorumQuorumQuorumQuorumQuorumAutobiographerQuorum
QuorumQuorumAutobiographerQuorumQuorumQuorumQuorumQuorumQuorumAutobiographerQuor
umQuorumQuorumAutobiographerQuorumQuorumQuorumAutobiographerQuorumMortarboardQuo
rumQuorumAutobiographerQuorumQuorumQuorumQuorumAutobiographerQuorumMortarboardQu
orumQuorumAutobiographerQuorumQuorumQuorumQuorumJongwareQuorumQuorumCommentatorQ
uorumQuorumAutobiographerQuorumQuorumQuorumQuorumQuorumQuorumQuorumAutobiographe
rQuorumQuorumAutobiographerQuorumQuorumProofreaderJongwareQuorumQuorumQuorumQuor
umQuorumQuorumMortarboardJongwareQuorumProofreaderCommentatorSuffrageQuorumQuoru
mQuorumQuorumJongwareQuorumAutobiographerSuffrageCommentatorCaucusCriticCleanupQ
uorumQuorumMortarboardAutobiographerCommentatorQuorumConstituentCriticCriticQuor
umQuorumQuorumQuorumAutobiographerJongwareCleanupSupporterInvestorCriticCleanupQ
uorumQuorumQuorumQuorumAutobiographerFanaticCleanupFanaticInvestorCriticCleanupQ
uorumQuorumQuorumQuorumAutobiographerCriticInformedSupporterCriticCriticInformed
QuorumQuorumQuorumQuorumAutobiographerCriticQuorumSupporterInvestorFanaticQuorum
QuorumQuorumQuorumQuorumAutobiographerCriticCleanupCommentatorQuorumDeputyQuorum
QuorumQuorumQuorumQuorumAutobiographerCriticInvestorCaucusInformedDeputyQuorumQu
orumQuorumQuorumQuorumQuorumCommentatorCriticCriticCitizen PatrolQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumAutobiographerCriticCriticCitizen PatrolQuorumQuorumQ
uorumQuorumQuorumQuorumQuorumAutobiographerCriticCriticCitizen PatrolStewardQuor
umQuorumQuorumQuorumQuorumAutobiographerConstituentCitizen PatrolCaucusQuorumQuo
rumQuorumQuorumQuorumQuorumAutobiographerInvestorCriticConstituentQuorumQuorumQu
orumQuorumQuorumQuorumQuorumCommentatorConstituentCleanupCaucusCleanupQuorumQuor
umQuorumQuorumQuorumQuorumAutobiographerInvestorCleanupSupporterInformedQuorumQu
orumQuorumQuorumAutobiographerCommentatorCriticInformedJongwareJongwareQuorumQuo
rumQuorumQuorumAutobiographerCommentatorCriticInvestorJongwareJongwareQuorumQuor
umQuorumQuorumAutobiographerFanaticInvestorCriticInformedCleanupQuorumQuorumQuor
umQuorumQuorumQuorumDeputySupporterInvestorConstituentCaucusQuorumQuorumQuorumQu
orumQuorumQuorumAutobiographerCriticInvestorCriticSupporterQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumAutobiographerInvestorInvestorCleanupQuorumQuorumQuorumQuor
umQuorumQuorumQuorumQuorumCommentatorInvestorInvestorQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumCommentatorInvestorInvestorQuorumQuorumQuorumQuorumQu
orumQuorumQuorumQuorumQuorumCommentatorInvestorCleanupStewardQuorumQuorumQuorumQ
uorumQuorumQuorumQuorumQuorumCommentatorInvestorJongwareQuorumQuorumQuorumQuorum
QuorumQuorumQuorumQuorumQuorumAutobiographerCleanupQuorumQuorumQuorumQuorumQuoru
mQuorumQuorumQuorumQuorumQuorumAutobiographerSuffrageQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumQuorumAutobiographerDeputyQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumQuorumQuorumAutobiographerQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuoru

Вам нужно немного пощупать; при небольшом размере выглядит так:

textvatar(tm)

Вот как я его создал.

Шаг 1: найдите подходящее изображение;

Шаг 2: преобразование в оттенки серого,

Шаг 3: преобразование серого в крайние значения. Этот шаг должен обеспечить, чтобы диапазон ввода (значения серого) использовал полный диапазон от 0 до 255.

Шаг 4: измените размер изображения для наилучшего соответствия. Я выбрал 80x40 и намеренно раздавил изображение наполовину. Это потому, что "текст" обычно выше, чем широкий. Для разных шрифтов нужны разные пропорции! 80 будет числом символов в строке, 40 - это общее количество строк.

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

Промежуточный шаг, увеличенный на 400%, чтобы вы могли видеть пиксели:

deformed grayscale image

Шаг 5: Найдите себе моноширинный растровый шрифт. Я нашел хороший 8x8, где-то в сети. Возможно, более крупный размер может работать лучше, но только очень незначительный, потому что ограничивающим фактором являются ваши строки, а не шрифт.

Шаг 6: Вычислить "серые" значения для каждого из символов ASCII. Это число черных пикселей, деленное на общее количество пикселей. Для лучшего распространения я разделил результат для каждого символа на максимум, поэтому самое низкое значение 0 (для пробела), а наивысшее - 1 (что оказалось для M, но это зависит от шрифт). Затем я умножил значения на 255, поэтому он имитирует значения оттенков серого. Наконец, поскольку эти значения были обратными тем, что были в изображении в оттенках серого, я заменил их на 255-value.

В промежутке времени я много тестировал, чтобы убедиться, что моя первоначальная идея все еще звучит. Вот дамп моего тестового изображения с использованием простого перевода с серого на символ:

0MMMMMM00000000000RDRRDDDDDDDDDDDDDDR#@RRRRR#[email protected]@[email protected]&44&#00#0000
MMMMMM000000000000RDRDDDDDDDDDDDDDDDR0####0R&&&&&&[email protected]@#########[email protected]
MMM000#00000000000RDRRDDDDDDDDDDD&&&R00#[email protected]&[email protected]####[email protected]#0000000##0#RD4PP&[email protected]
M000#0000000000000RDDDDDDDDDDD&D&&D&R###[email protected]#0000#@@##@@@@@##000000R&2FPP4R#000
00###0000000000000RDDDDDDDDDDDDDD&&&D0000#@##@##00##@RDD&DRRRR#[email protected]@00
00#000000000000000RDDDDDDDDDD&D&D&&&[email protected]@@##00#R&4&44DDDRRR#0000##R&[email protected]
00##00#00000000000RDDDDDDDDDDD&&&&&&RDRRRRRRRR##@@RR&F4&[email protected]@#0000000#&44&&4FD
000000000000000000DDDDDDD&&&DD&&&D&&R##@RRRRRRRRRRRRD&DRD&44DD&[email protected]#00000RDDRR4F4
000000000000000000RDDDDDDDRRDRDDD&[email protected]###@R&4&4444PP4DRRR&P22FF2FP&[email protected]
[email protected]@@R&&[email protected]&P2$33*33*4RRRDP23$*33$$**333*$$2D#@4DR
[email protected]#DDRRRRRD&D4*$$%%ff3F&DRRR2$1%$$%11ff%ll1l;'IRDF&R
[email protected]#0#00000R4444DRRRR&3llf33$32PDRR&3Ii(i%fIlI1Il!ii/' .FF4DR
0000000#0000000000#@@#0MM0M00#RP22*24DR4$l!!!I%*P4DDP31i===/lf1Illi((ii=;..*F4RR
000000000000000000#@@##[email protected]*$%*2PFflilllI1f3*2PF3fIi/=(lf$fIi/======;iF&P4R
000000000000000000##@##00000000&3f$2F2*1i!ll1$2P4RD&4P*$$%!ii!I!ii/=/=;;;(*&R4FR
000000000000000000#####00000000D%I3$$fflil!l1$3%3&RD2PP*$%1ll!lI1%f$3fI="1RRRRDR
000000000000000000#####00000000Rf1f%II1IIl!l!!1%ff$*FFFF*%((lf**F4&D&PPFf2RRRRRR
00000000000000000M#####0000000003!i11II11l!!!(ilIIl1f$f%I="=1FP4&@#R&DP22&[email protected]#@
0000000000000000000###0000000000#$i/iIIIl!!i=;"";===;""""";i12FFP4&4*4&*[email protected]@R
000000000000000000M000000000000000R4FF1l!i(=;"''"""""'...';/i$3$f$$33$$$4DRRRRRR
[email protected](/ii/=;;=ilI;"'...'=;I%[email protected]@[email protected]#0
000000000000000000000000000M00000000&11!=/li(i!I%%Ii;... .";"!I!!I1ff$*R0#@#0000
000000000000000000000000M0000M000000$lli=//=/i!lIi/l%I/""=//'=Iiiil%[email protected]#@00000
000000000000000000000000M0M0M00000MRl!!!/=;;/l%fl!lI%3233$ff%11II%%f*[email protected]##00000
00000000000000000M000000M0MM00000003=illl!!iI%11%%f%f$32FF22$1%ff$f2DD4PR0000000
00000000000000000M00M0#RRRRRRRRRRRPi/(lllI1I(=;=iI$2222FPFFP*$fff%FRR2322&R00000
00000000000000000MM00#RRD&&&&&444*l/(i!ll!i!==/(il%$33*2FFF23$ff*4DDDF33*22&0000
[email protected]@@@@@#&f!((/i!!!lI!i=;;/!l1f32*3$$$ff$2&[email protected]###@##00##000
0000000000000000#[email protected]@4F4R0R3!////(iii!!l%Ii=;"=l%f$333$ff%$4############00000#
0000000000000000000000#@@@#0Di/ii(=/(i!!!lI%%1!(iI%$**[email protected]
00000000000000000000000000000R3l((/((ii!lll!I%$3$f$$$$$3$f20M0000000000000000000
000000000000000000000000000000#2l///((!!l!!i!!1%f%f$3$f%%fR000000000000000000000
00000000000000#00000000000000000&l//(i!llllII1%%%1I1ff1I1&000000000000000000#00#
M00000000000000000000000000000000R3l!!llII111%%%%%%f$$f%2000000000000000000RR00#
0000#000000000000000000000000000000R*%1II111%%ffff$33fffR000000000000000000##00#
0####000000000000000000000000000000M0RF3ff$$$fff$$33$ff&00000000000#000000##000R
#00000000#00000000000000000000000000000R4FFFF22*****[email protected]&
00###[email protected]&&&4P*34#00000000000000000000000#R
00000####000000000000000000000000000000000000##@RRDDR00000000000000M0000000000##
00000000#000000000000000000000000000000000000000000000000000000000000000000000##

Это похоже на то, что выводится ваше среднее изображение на конвертер ASCII art.

Шаг 7: найдите список строк для использования.

Шаг 8: начиная с левого верхнего угла, проверьте охват каждой из ваших строк против изображения. Распечатайте наилучшую посадку, увеличьте положение по длине этой строки, повторите до конца. (Если вы не хотите дубликатов, вы удаляете строку из своего пула в этот момент.)

Шаг 9: прибыль!


Для теста покрытия я использовал сумму (source - dest)² для каждого символа/пикселя, деленный на длину строки: lower = better - наименьшая разница между строкой и пунктом назначения.

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

Возможное улучшение заключается в проверке последовательности строк, то есть вместо жадного подхода здесь, используйте подход с динамическим программированием, как и Дональд Кнут разработал, чтобы решить лучшие перерывы в текстовом переводе текста.