Генерация изображения из произвольных строк
Я видел множество конвертеров изображений в ascii art, но мне нужно немного сложнее. Вместо мозаичного изображения, созданного из символов ascii, мне нужно использовать предопределенный набор строк произвольной длины в качестве мозаичных плиток.
Я хорошо поработал над поиском, но я даже не уверен, как структурировать свой запрос? Существует ли что-нибудь подобное? И бонус, если он не будет использовать дубликаты при генерации изображения.
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
Вы должны немного щуриться; при небольшом размере это выглядит так:
Вот как я это создал.
Шаг 1: Найти подходящее изображение;
Шаг 2: преобразовать в оттенки серого;
Шаг 3: преобразовать серый в крайности. Этот шаг должен гарантировать, что входной диапазон (значения серого) использует полный диапазон от 0 до 255.
Шаг 4: измените размер изображения для наилучшего соответствия. Я выбрал 80x40, и намеренно раздавил изображение пополам. Это потому, что "текст" обычно выше, чем он широкий. Разные шрифты нуждаются в разных пропорциях! 80
будет количество символов в строке, 40
общее количество строк.
Я использовал Photoshop для описанных выше шагов просто потому, что не хотел писать код для него. Это не сложно или что-то еще, если у вас есть доступ к необработанным данным изображения, но это много работы и не интересно.
Промежуточный шаг, увеличенный на 400%, чтобы вы могли видеть пиксели:
Шаг 5: Найдите себе моноширинный растровый шрифт. Я нашел хороший 8x8 где-то в сети. Возможно, больший размер может работать лучше, но только очень незначительно, потому что ограничивающим фактором являются ваши строки, а не шрифт.
Шаг 6: Рассчитайте "серые" значения для каждого из символов ASCII. Это количество черных пикселей, деленное на общее количество пикселей. Для лучшего распространения я разделил результат для каждого символа на максимальное найденное, поэтому самое низкое значение 0
(для космоса) и самый высокий 1
(что случилось для M
, но это зависит от шрифта). Затем я умножил значения на 255, поэтому они имитируют значения в градациях серого. Наконец, поскольку эти значения были обратными значениям на изображении в оттенках серого, я заменил их на 255-value
,
В промежутках между прочим я провел много тестов, чтобы убедиться, что моя первоначальная идея была еще верна. Вот дамп моего тестового изображения с использованием простого перевода текста в серый:
0MMMMMM00000000000RDRRDDDDDDDDDDDDDDR#@RRRRR#00000RRR@RRRRR@RR@RRRRD&44�#0000
MMMMMM000000000000RDRDDDDDDDDDDDDDDDR0####0R&&&&&&DR@RRR@#########RRDDD4@0000000
MMM000#00000000000RDRRDDDDDDDDDDD&&&R00#000@&DR@####RR@#0000000##0#RD4PP&R@00000
M000#0000000000000RDDDDDDDDDDD&D&&D&R###0000@#0000#@@##@@@@@##000000R&2FPP4R#000
00###0000000000000RDDDDDDDDDDDDDD&&&D0000#@##@##00##@RDD&DRRRR#00000@R4FFP4PD@00
00#000000000000000RDDDDDDDDDD&D&D&&&DRRRRDR@@@##00#R&4&44DDDRRR#0000##R&FPP4PRR@
00##00#00000000000RDDDDDDDDDDD&&&&&&RDRRRRRRRR##@@RR&F4&RDDRR@@#0000000#&44&&4FD
000000000000000000DDDDDDD&&&DD&&&D&&R##@RRRRRRRRRRRRD&DRD&44DD&RR@#00000RDDRR4F4
000000000000000000RDDDDDDDRRDRDDD&DR@###@R&4&4444PP4DRRR&P22FF2FP&DRRRRRRRR@R4FD
000000000000000000RRRRRRR@00@@R&&DRRRRRR@&P2$33*33*4RRRDP23$*33$$**333*$$2D#@4DR
000000000000000000RRRRRRR@0000#DDRRRRRD&D4*$$%%ff3F&DRRR2$1%$$%11ff%ll1l;'IRDF&R
000000000000000000RRR@#0#00000R4444DRRRR&3llf33$32PDRR&3Ii(i%fIlI1Il!ii/' .FF4DR
0000000#0000000000#@@#0MM0M00#RP22*24DR4$l!!!I%*P4DDP31i===/lf1Illi((ii=;..*F4RR
000000000000000000#@@##0M00000@F*$%*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&RRR@#@
0000000000000000000###0000000000#$i/iIIIl!!i=;"";===;""""";i12FFP4&4*4&*FRRRR@@R
000000000000000000M000000000000000R4FF1l!i(=;"''"""""'...';/i$3$f$$33$$$4DRRRRRR
000000000000000000000000000M00000000@21l(/ii/=;;=ilI;"'...'=;I%11f$$$ffP@@RR@R#0
000000000000000000000000000M00000000&11!=/li(i!I%%Ii;... .";"!I!!I1ff$*R0#@#0000
000000000000000000000000M0000M000000$lli=//=/i!lIi/l%I/""=//'=Iiiil%f$PR@#@00000
000000000000000000000000M0M0M00000MRl!!!/=;;/l%fl!lI%3233$ff%11II%%f*2PP@##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
0000000000000000000000@R@@@@@#&f!((/i!!!lI!i=;;/!l1f32*3$$$ff$2&RR@###@##00##000
0000000000000000#RRRR@@4F4R0R3!////(iii!!l%Ii=;"=l%f$333$ff%$4############00000#
0000000000000000000000#@@@#0Di/ii(=/(i!!!lI%%1!(iI%$**$$fffF@0000000000000000000
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*****3fF00000000000000000000000@&
00###000000000000000000000000000000000000@RRDD&&&4P*34#00000000000000000000000#R
00000####000000000000000000000000000000000000##@RRDDR00000000000000M0000000000##
00000000#000000000000000000000000000000000000000000000000000000000000000000000##
Это похоже на то, что выводит ваше среднее изображение в ASCII-конвертер.
Шаг 7: найдите себе список строк для использования.
Шаг 8: начиная с левого верхнего угла, проверьте покрытие каждой из ваших строк на изображении. Распечатайте лучшее соответствие, увеличьте положение на длину этой строки, повторите до конца. (Если вы не хотите дубликатов, вы удалите строку из своего пула на этом этапе.)
Шаг 9: прибыль!
Для теста покрытия я использовал сумму (source - dest)²
для каждого символа / пикселя, деленного на длину строки: ниже = лучше - наименьшая разница между строкой и пунктом назначения.
Я не рассматривал красивые окончания строк здесь. Я экспериментировал с предоставлением отрицательного бонуса, если слово точно заполняло строку, но разница в выводе была незначительной. Это может все еще работать с большим набором строк.
Возможное улучшение состоит в том, чтобы протестировать последовательность строк, то есть вместо жадного подхода здесь использовать подход динамического программирования, во многом как Дональд Кнут, разработанный для определения лучших разрывов в тексте переноса слов.