Найти в Дзене
Kitchen Python 3

Кухня Python 3: "Формула Таппера"

Оглавление

На нашу кухню попал рецепт интересной формулы, а именно формулы Таппера. Если Вы хотите декодировать растровое изображение, то лучше формулы Вам и не найти. Попробуем же сварить её.

ВНИМАНИЕ: Сервис удаляет начальные пробелы в строках, поэтому приходится использовать символ нижнего подчеркивания вместо каждого пробела. И я буду предоставлять скриншоты общего кода для наглядности

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

-2

Формула применяется для декодирования растровых изображений, кодируемая в константе k.

Константа k — монохромный растр, используемый в формуле как двоичное число, умноженное на 17.

Для создания константы k из изображения необходимо:

1) Представить изображение в растровом виде на поле 106*17;

2) Заменить, двигаясь снизу-вверх и слева-направо, закрашенные клетки на "1", а пустые на "0";

3) Перевести полученное число в десятичную систему счисления;

4) Умножить число на 17;

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

Само k будет выглядеть так:

48584506361897134235820959624942020445814005879832445494830930

85061934704708809928450644769865524364849997247024915119110411

60573917740785691975432657185544205721044573588368182982375413

96343382251994521916512843483329051311931999535024137587652392

64874613394906870130562295813219481113685339535565290850023875

09285689269455597428154638651073004910672305893358605254409666

43512653493636439571255656959368151843348576052669401612512669

51421550539554519153785457525756590740540157929001765967965480

064427829131488548259914721248506352686630476300

Для изображения:

-3

Для ремарки скажу, что мы будем варить полностью алгоритм, от начала до конца. Логично, что действие с переводом в 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

-4

Пояснение: Нули добавляем, если вначале изображения имелись пустые клетки. Так как python удалит начальные нули в числе.

3. Подгрузите изображение(с той же папки, где и ваш скрипт), конвертируйте в монохром и проверьте на подходящие размеры.

im = Image.open('kitch.png') # Подгрузка изображения
im = im.convert('1') # Конвертация в монохром
if im.width > 106 or im.height > 17:
____print("Размеры изображения не подходят для решения задачи")
____exit(0)

-5

Пояснение: Для рецепта я использовал это изображение. Так как сервис не дал загрузить изображение в размере 106х17, пришлось увеличить его здесь на 3 - 318х51. Имейте это ввиду.

-6
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

-7

Теперь у нас есть полноценно приготовленное 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),))

-8

Пояснение:

Второй цикл двигается по строке. То есть разбив на 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")

-9

До и после:

-10
-11

Пришлось так же увеличить изображение.

-12

Наука
7 млн интересуются