На нашу кухню попал рецепт интересной формулы, а именно формулы Таппера. Если Вы хотите декодировать растровое изображение, то лучше формулы Вам и не найти. Попробуем же сварить её.
ВНИМАНИЕ: Сервис удаляет начальные пробелы в строках, поэтому приходится использовать символ нижнего подчеркивания вместо каждого пробела. И я буду предоставлять скриншоты общего кода для наглядности
Начнем с того, что поясним вкратце, что же эта за формула. Если вы хотите узнать о ней подробнее, то можете заглянуть на википедию.
Формула применяется для декодирования растровых изображений, кодируемая в константе k.
Константа k — монохромный растр, используемый в формуле как двоичное число, умноженное на 17.
Для создания константы k из изображения необходимо:
1) Представить изображение в растровом виде на поле 106*17;
2) Заменить, двигаясь снизу-вверх и слева-направо, закрашенные клетки на "1", а пустые на "0";
3) Перевести полученное число в десятичную систему счисления;
4) Умножить число на 17;
Таким же образом, но в обратном порядке, можно получить изображение из константы k.
Само k будет выглядеть так:
48584506361897134235820959624942020445814005879832445494830930
85061934704708809928450644769865524364849997247024915119110411
60573917740785691975432657185544205721044573588368182982375413
96343382251994521916512843483329051311931999535024137587652392
64874613394906870130562295813219481113685339535565290850023875
09285689269455597428154638651073004910672305893358605254409666
43512653493636439571255656959368151843348576052669401612512669
51421550539554519153785457525756590740540157929001765967965480
064427829131488548259914721248506352686630476300
Для изображения:
Для ремарки скажу, что мы будем варить полностью алгоритм, от начала до конца. Логично, что действие с переводом в k можно опустить, но для порядку надо сделать. Ничего сложного же? Так давайте приступим!
Ингредиенты:
Python 3
библиотека PIL (Pillow) для работы с изображениями
библиотека textwrap для удобства разбития строки на 17 равных частей
Формула Таппера
Pycharm по вкусу
Приготовление:
1. Подключите необходимые библиотеки
from PIL import Image, ImageDraw
import textwrap
2. Создайте функцию convert_k_to_bin для конвертации числа k в двоичное число.
def convert_k_to_bin(k):
____k //= 17 # Делим на 17 без остатка
____binary = bin(k)[2:] # Удаляем первые два элемента
____binary = ("0" * (1802 - len(binary))) + binary # Добавляем нули в начало
____return binary
Пояснение: Нули добавляем, если вначале изображения имелись пустые клетки. Так как python удалит начальные нули в числе.
3. Подгрузите изображение(с той же папки, где и ваш скрипт), конвертируйте в монохром и проверьте на подходящие размеры.
im = Image.open('kitch.png') # Подгрузка изображения
im = im.convert('1') # Конвертация в монохром
if im.width > 106 or im.height > 17:
____print("Размеры изображения не подходят для решения задачи")
____exit(0)
Пояснение: Для рецепта я использовал это изображение. Так как сервис не дал загрузить изображение в размере 106х17, пришлось увеличить его здесь на 3 - 318х51. Имейте это ввиду.
4. Считывайте изображение и заносите в line_bytes двоичный код, где 1 - это закрашенный, 0 - пустота. Естественно, преобразуйте получившуюся строку в десятичный и умножьте на 17, получив k
line_bytes = ""
for x in range(105,-1,-1):
____for y in range(17):
________if im.getpixel((x,y)) > 127:
____________line_bytes += '1'
________else:
____________line_bytes += '0'
k = int(line_bytes, 2)*17
Теперь у нас есть полноценно приготовленное k. До конца варки осталось совсем немного. Преобразовать его обратно и вывести наше изображение в image.png.
5. Преобразуйте k в двоичный код с помощью функции, сваренной до этого convert_k_to_bin()
bynary = convert_k_to_bin(int(k))
6. Создайте новое изображение, в котором мы будем рисовать
image = Image.new("1", (106, 17), (0))
draw = image.load()
7. Теперь самое интересное. Варка возможна двумя способами, более понятным или более, на мой взгляд красивым. Приготовим более красивым способом, а в пояснении разберем более понятный. Создайте два цикла, один из которых вложенный и во вложенном цикле рисуйте изображение.
for ind_x, sub_bynary in enumerate(reversed(textwrap.wrap(bynary,17))):
____for ind_y, symb_bynary in enumerate(sub_bynary):
________image.putpixel(xy = (ind_x, ind_y), value = (int(symb_bynary),))
Пояснение:
Второй цикл двигается по строке. То есть разбив на 17 частей, мы получаем в одной из части, к примеру "0001011100011100...". Ее то мы и перебираем
textwrap.wrap(bynary,17) - разбиваем нашу строку двоичного кода по 17 равных частей
reversed - делаем обратный цикл, иначе получите перевернутое изображение
enumerate - получаем счетчик-элемент.
image.putpixel(xy = (ind_x, ind_y), value = (int(symb_bynary),)) - рисуем в координатах xy наш пиксель symb_bynary
Второй способ:
p = len(bynary) - 1
for x in range(106):
____for y in range(17):
________image.putpixel(xy = (x, 16 - y), value = (int(bynary[y]),))
________p -= 1
По мне, данный способ, слишком прост и не даст никаких новых навыков с обращением Питона. Каждому своё и на свой вкус.
8. Осталось самое сложное, будьте предельно внимательны! От Вас потребуется много усилий. Сохраните получившееся изображение.
image.save("image.png") #сохраняем изображение
Готовое блюдо:
from PIL import Image, ImageDraw
import textwrap
def convert_k_to_bin(k):
____k //= 17
____ binary = bin(k)[2:]
____ binary = ("0" * (1802 - len(binary))) + binary
____return binary
im = Image.open('kitch.png')
im = im.convert('1')
if im.width > 106 or im.height > 17:
____print("Размеры изображения не подходят для решения задачи")
____exit(0)
line_bytes = ""
for x in range(105,-1,-1):
____for y in range(17):
________if im.getpixel((x,y)) > 127:
____________line_bytes += '1'
________else:
____________line_bytes += '0'
k = int(line_bytes,2)*17
bynary = convert_k_to_bin(int(k))
image = Image.new("1", (106, 17), (0))
draw = image.load()
for ind_x, sub_bynary in enumerate(reversed(textwrap.wrap(bynary,17))):
____for ind_y, symb_bynary in enumerate(sub_bynary):
________image.putpixel(xy = (ind_x, ind_y), value = (int(symb_bynary),))
image.save("image.png")
До и после:
Пришлось так же увеличить изображение.