как перевести картинку в двоичный код

Содержание
  1. Урок 9 Кодирование рисунков § 12. Кодирование рисунков: растровый метод § 13. Кодирование рисунков: другие методы
  2. Содержание урока
  3. § 12. Кодирование рисунков: растровый метод
  4. Что такое растровое кодирование?
  5. Ключевые слова:
  6. Binary Image 0 1
  7. Image to Binary Converter
  8. Binary to Image Generator
  9. Answers to Questions (FAQ)
  10. What is a binary image? (Definition)
  11. How to encode an image in binary?
  12. What are accepted image formats?
  13. What is image binarization? (Image 1-bit)
  14. Are 0 codes for black or for white?
  15. Source code
  16. Questions / Comments
  17. Картинка, которая одновременно является кодом на Javascript
  18. Выбор подходящего типа изображения
  19. Выбор подходящих размеров изображения
  20. Засовываем в файл скрипт
  21. Подчищаем двоичные данные
  22. Боремся с искажениями
  23. Завершаем файл
  24. Уговариваем браузер исполнить изображение
  25. Декодируем JPEG-изображение с помощью Python
  26. Введение
  27. Разные части JPEG
  28. Начало и конец файла
  29. Кодирование JPEG
  30. Цветовое пространство JPEG
  31. Дискретное косинусное преобразование и квантование
  32. Зигзаг
  33. Кодирование длин серий и дельта-кодирование
  34. Кодирование Хаффмана
  35. Декодирование JPEG
  36. Извлечение таблиц Хаффмана
  37. Декодирование таблицы квантования
  38. Декодирование начала кадра
  39. Декодирование Start of Scan
  40. Вывод изображения на экран
  41. Заключение
  42. Дополнительные материалы

Урок 9
Кодирование рисунков
§ 12. Кодирование рисунков: растровый метод
§ 13. Кодирование рисунков: другие методы

Содержание урока

§ 12. Кодирование рисунков: растровый метод

Что такое растровое кодирование?

§ 13. Кодирование рисунков: другие методы

§ 12. Кодирование рисунков: растровый метод

Что такое растровое кодирование?

Ключевые слова:

• растр
• пиксель
• разрешение
• цветовая модель RGB
• цветовая модель CMYK
• цветовая модель HSB
• глубина цвета
• цветовая палитра

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

И линия, и область состоят из бесконечного числа точек. Цвет каждой из этих точек нам нужно как-то закодировать. Так как точек бесконечно много, для этого нужно бесконечно много памяти, поэтому таким способом изображение закодировать не удастся. Однако «поточечную» идею всё-таки можно использовать.

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

ur 09 01

У нас получился растровый рисунок, состоящий из квадратиков-пикселей.

galochka znak2Пиксель (англ. pixel: picture element — элемент рисунка) — это наименьший элемент рисунка, для которого можно задать свой цвет.

Разбив рисунок на квадратики, мы выполнили его дискретизацию. Действительно, у нас был непрерывный рисунок — изображение ромба. В результате мы получили дискретный объект — набор пикселей.

Двоичный код для чёрно-белого рисунка, полученного в результате дискретизации, можно построить следующим образом:

1) кодируем белые пиксели нулями, а чёрные — единицами 1) ;
2) выписываем строки полученной таблицы одну за другой.

1) Можно сделать и наоборот, чёрные пиксели обозначить нулями, а белые — единицами.

Покажем это на простом примере (рис. 2.20).

ur 09 02

Ширина этого рисунка — 8 пикселей, поэтому каждая строка таблицы состоит из 8 двоичных разрядов — битов. Чтобы не писать очень длинную цепочку нулей и единиц, удобно использовать шестнадцатеричную систему счисления, закодировав 4 соседних бита (тетраду) одной шестнадцатеричной цифрой. Например, для первой строки получаем код 1А16:

0 0 0 1 1 0 1 0
1 A

а для всего рисунка: 1A2642FF425A5A7E16.

ur 09 03

Используя полученный шестнадцатеричный код картинки, подсчитайте её информационный объём в битах и байтах.

Очень важно понять, что мы приобрели и что потеряли в результате дискретизации. Самое главное — мы смогли закодировать изображение в двоичном коде. Однако при этом рисунок исказился — вместо ромба мы получили набор квадратиков. Причина искажения в том, что в некоторых квадратиках части исходного рисунка были закрашены разными цветами, а в закодированном изображении каждый пиксель обязательно имеет один цвет. Таким образом, часть исходной информации при кодировании была потеряна. Это проявится, например, при увеличении рисунка — квадратики увеличиваются и рисунок ещё больше искажается. Чтобы уменьшить потери информации, нужно уменьшать размер пикселя, т. е. увеличивать разрешение.

galochka znak2Разрешение — это количество пикселей, приходящихся на единицу линейного размера изображения (чаще всего — на 1 дюйм).

Разрешение обычно измеряется в пикселях на дюйм (используется английское обозначение ppi: — pixels per inch). Например, разрешение 254 ppi означает, что на дюйм приходится 254 пикселя.

Чем больше разрешение, тем точнее кодируется рисунок (меньше информации теряется), однако одновременно растёт и объём файла.

Одна и та же картинка была отсканирована дважды: в первый раз с разрешением 300 ppi, а второй раз — с разрешением 600 ppi. Что можно сказать о размерах полученных файлов?

Существуют два основных способа получения растровых изображений:

1) ввод с помощью какого-либо устройства, например сканера, цифрового фотоаппарата или веб-камеры; напомним, что при сканировании происходит преобразование информации в компьютерные данные (оцифровка);

2) создание рисунка с помощью какой-либо программы.

Используя дополнительные источники, найдите ответы на вопросы.

— Чему равен один дюйм в миллиметрах?
— Если отсканировать рисунок с разрешением 254 ppi, какой размер будет иметь изображение одного пикселя?
— Какие размеры в пикселях будет иметь изображение рисунка размером 10 х 15 см, если отсканировать его с разрешением 254 ppi?

Следующая страница smotri 1Как кодируется цвет?

Cкачать материалы урока
skachat

Источник

Binary Image 0 1

Image to Binary Converter

Binary to Image Generator

Answers to Questions (FAQ)

What is a binary image? (Definition)

A binary image is a digital image whose pixels have 2 colors (usually black or white). It is therefore possible to represent a binary image as a series/array of 0 and 1.

How to encode an image in binary?

Some people see it as a form of binary art: the pixel art.

What are accepted image formats?

All web image formats (JPG, PNG, GIF, etc.) are accepted, but it is preferable to use a format that uses lossless compression (PNG, BMP, etc.), because in these cases the data of each pixel color is not altered.

Many icons of 16×16, 32×32, 64×64 size make excellent formats.

What is image binarization? (Image 1-bit)

Binarization is the action of binarizing (make binary with 2 elements) data.

From a practical point of view, an image with 2 colors (coded on 1 bit) is quick to store, each pixel is either 0 or 1.

Are 0 codes for black or for white?

There is no standard for 1-bit images, but generally 0 codes for black and 1 for white, but nothing prevents the use of 1 for black and 0 for white.

NB: An 8-bit image codes 0 for black and 255 for white.

Source code

Please, check our dCode Discord community for help requests!
NB: for encrypted messages, test our automatic cipher identifier!

Questions / Comments

Thanks to your feedback and relevant comments, dCode has developed the best ‘Binary Image 0 1’ tool, so feel free to write! Thank you!

Источник

Картинка, которая одновременно является кодом на Javascript

image loader

Изображения обычно хранятся как двоичные файлы, а файл Javascript по сути является обычным текстом. Оба типа файлов должны следовать собственным правилам: изображения имеют конкретный формат файла, определённым образом кодирующий данные. Для того, чтобы файлы Javascript можно было исполнять, они должны следовать определённому синтаксису. Я задался вопросом: можно ли создать файл изображения, одновременно являющийся допустимым синтаксисом Javascript, чтобы его можно было исполнять?

Прежде чем вы продолжите чтение, крайне рекомендую изучить эту песочницу кода с результатами моих экспериментов:

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

Выбор подходящего типа изображения

К сожалению, изображения содержат множество двоичных данных, которые при интерпретации в качестве Javascript будут выдавать ошибку. Поэтому моя первая мысль заключалась в следующем: что если просто поместить все данные изображения в большой комментарий, примерно так:

Этот заголовок файла привёл меня к следующей идее: что если использовать эту последовательность байтов как имя переменной и присвоить ей значение длинной строки:

К сожалению, большинство последовательностей байтов в заголовках файлов изображений содержат непечатаемые символы, которые нельзя использовать в именах переменных. Но есть один формат, который мы можем использовать: GIF. Блок заголовка GIF имеет вид 47 49 46 38 39 61, что удобно преобразуется в ASCII в строку GIF89a — абсолютно допустимое имя переменной!

Выбор подходящих размеров изображения

Теперь, когда мы нашли формат изображения, начинающийся с допустимого имени переменной, нам нужно добавить символы знака равенства и обратного апострофа (backtick). Следовательно следующими четырьмя байтами файла будут: 3D 09 60 04

image loader

Первые байты изображения

В формате GIF четыре байта после заголовка определяют размеры изображения. Нам нужно уместить в них 3D (знак равенства) и 60 (обратный апостроф, открывающий строку). В GIF используется порядок little endian, поэтому второй и четвёртый символы имеют огромное влияние на размеры изображения. Они должны быть как можно меньше, чтобы изображение не получилось шириной и высотой в десятки тысяч пикселей. Следовательно, нам нужно хранить большие байты 3D и 60 в наименее значимых байтах.

Наименьший пробельный символ — это 09 (символ горизонтальной табуляции). Он даёт нам ширину изображения 3D 09, что в little endian равно 2365; немного шире, чем бы мне хотелось, но всё равно вполне приемлемо.

Для второго байта высоты можно выбрать значение, дающее хорошее соотношение сторон. Я выбрал 04, что даёт нам высоту 60 04, или 1120 пикселей.

Засовываем в файл скрипт

Пока наш исполняемый GIF почти ничего не делает. Он просто присваивает глобальной переменной GIF89a длинную строку. Мы хотим, чтобы происходило что-нибудь интересное! Основная часть данных внутри GIF используется для кодирования изображения, поэтому если мы попробуем вставить туда Javascript, то изображение, вероятно, будет сильно искажённым. Но по какой-то причине формат GIF содержит нечто под названием Comment Extension. Это место для хранения метаданных, которые не интерпретируются декодером GIF — идеальное место для нашей Javascript-логики.

Это расширение для комментариев находится сразу после таблицы цветов GIF. Поскольку мы можем поместить туда любое содержимое, можно запросто закрыть строку GIF89a, добавить весь Javascript, а затем начать многострочный блок комментария, чтобы остальная часть изображения не влияла на парсер Javascript.

В конечном итоге наш файл может выглядеть следующим образом:

Однако существует небольшое ограничение: хотя сам блок комментария может иметь любой размер, он состоит из нескольких подблоков, и максимальный размер каждого из них составляет 255. Между подблоками есть один байт, определяющий длину следующего подблока. Поэтому чтобы уместить туда большой скрипт, его нужно разделить на мелкие фрагменты, примерно вот так:

Шестнадцатеричные коды в комментариях — это байты, определяющие размер следующего подблока. Они не относятся к Javascript, но обязательны для формата файла GIF. Чтобы они не мешали остальной части кода, их нужно поместить в комментарии. Я написал небольшой скрипт, обрабатывающий фрагменты скрипта и добавляющий их в файл изображения:

Подчищаем двоичные данные

Теперь, когда у нас есть базовая структура, нам нужно сделать так, чтобы двоичные данные изображения не испортили синтаксис кода. Как говорилось в предыдущем разделе, файл состоит из трёх разделов: в первом выполняется присваивание значения переменной GIF89a, второй — это код на Javascript, а третий — комментарий из нескольких строк.

Давайте взглянем на первую часть с присвоением значения переменной:

Боремся с искажениями

В конце Javascript-кода мы открыли многострочный комментарий, чтобы двоичные данные изображения не влияли на парсинг Javascript:

image loader

Завершаем файл

Нам осталась последняя операция — завершение файла. Файл должен завершаться байтами 00 3B, поэтому нам нужно завершить комментарий раньше. Поскольку это конец файла и любые потенциальные повреждения будут не особо заметны, я просто завершил комментарий из блоков и добавил однострочный комментарий, чтобы конец файла не вызывал проблем при парсинге:

Уговариваем браузер исполнить изображение

Refused to execute script from ‘http://localhost:8080/image.gif’ because its MIME type (‘image/gif’) is not executable. [Отказ от исполнения скрипта из ‘http://localhost:8080/image.gif’, потому что его MIME-тип не является исполняемым.]

То есть браузер справедливо говорит: «Это изображение, я не буду его исполнять!». И в большинстве случаев это вполне уместно. Но мы всё равно хотим его исполнить. Решение заключается в том, чтобы просто не говорить браузеру, что это изображение. Для этого я написал небольшой сервер, передающий изображение без информации заголовка.

Без информации о MIME-типе из заголовка браузер не знает, что это изображение и делает именно то, что лучше всего подходит в контексте: отображает его как изображение в теге или исполняет как Javascript в теге

Источник

Декодируем JPEG-изображение с помощью Python

9p8uz00vyiu gcooou6mjpi6tcg

Введение

Зачем писать ещё одну статью про JPEG, когда об этом написаны уже сотни статей? Обычно в таких статьях авторы рассказывают лишь о том, что собой представляет формат. Вы не пишете код распаковки и декодирования. А если и напишете что-то, то на С/С++, и этот код будет недоступен широкому кругу людей. Я хочу нарушить эту традицию и показать вам с помощью Python 3, как работает базовый декодер JPEG. В его основе будет этот код, разработанный MIT, но я его сильно изменю ради удобочитаемости и понятности. Изменённый для этой статьи код вы найдёте в моём репозитории.

Разные части JPEG

Начнём с картинки, сделанной Ange Albertini. На ней перечислены все части простого JPEG-файла. Мы разберём каждый сегмент, и по мере чтения статьи вы не раз будете возвращаться к этой иллюстрации.

Почти каждый двоичный файл содержит несколько маркеров (или заголовков). Можете считать их своего рода закладками. Они крайне важны для работы с файлом и используются такими программами как file (на Mac и Linux), чтобы мы могли узнать подробности о файле. Маркеры указывают, где именно в файле хранится определённая информация. Чаще всего маркеры размещаются в соответствии со значением длины ( length ) конкретного сегмента.

Начало и конец файла

Мы будем работать с этим изображением:

Давайте напишем код для поиска маркеров начала и конца файла.

После раздела Start of Scan сразу идут данные сканированного изображения, у которых нет определённой длины. Они продолжаются до маркера «конец файла», так что пока мы вручную «ищем» его, когда находим маркер Start of Scan.

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

Кодирование JPEG

Сначала поговорим о базовых концепциях и методиках кодирования, которые используются в JPEG. А кодирование будет выполняться в обратном порядке. По моему опыту, без этого разобраться в декодировании будет трудно.

Иллюстрация ниже пока что для вас непонятно, однако я буду давать вам подсказки по мере изучения процесса кодирования и декодирования. Здесь показаны этапы JPEG-кодирования (источник):

Цветовое пространство JPEG

Согласно спецификации JPEG (ISO/IEC 10918-6:2013 (E), раздел 6.1):

Как в RGB каждый пиксель кодируется тремя байтами цветов (красного, зелёного и синего), так и в YUV используется три байта, однако их значение другое. Компонент Y определяет яркость цвета (luminance, или luma). U и V определяют цвет (chroma): U отвечает за долю синего цвета, а V — за долю красного.

Этот формат был разработан в те времена, когда телевидение ещё не было столь привычным, и инженеры хотели использовать один формат кодирования изображения и для цветного, и для чёрно-белого телевещания. Подробнее об этом читайте тут.

Дискретное косинусное преобразование и квантование

JPEG преобразует изображение в блоки 8×8 пикселей (называются MCU, Minimum Coding Unit — минимальные единицы кодирования), меняет диапазон значений пикселей так, чтобы в центре было значение 0, затем применяет к каждому блоку дискретное косинусное преобразование и сжимает результат с помощью квантования. Давайте разберёмся, что всё это означает.

Дискретное косинусное преобразование (ДКП) — это метод преобразования дискретных данных в комбинаций косинусных волн. Превращение картинки в набор косинусов на первый взгляд выглядит бесполезным занятием, но вы поймёте причину, когда узнаете о следующих этапах. ДКП берёт блок 8х8 пикселей и говорит нам, как воспроизвести этот блок с помощью матрицы из 8х8 косинусных функций. Подробнее тут.

Матрица выглядит так:

Мы применяем ДКП отдельно к каждому компоненту пикселя. В результате получаем матрицу коэффициентов 8х8, которая показывает вклад каждой (из всех 64) косинусной функции во входной матрице 8х8. В матрице коэффициентов ДКП самые большие значения обычно находятся в левом верхнем углу, а самые маленькие — в правом нижнем. Левый верхний угол представляет собой самую низкочастотную косинусную функцию, а правый нижний — самую высокочастотную.

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

Я нашёл отличное видео по этой теме. Посмотрите, если не понимаете смысла ДКП:

Все мы знаем, что JPEG — алгоритм сжатия с потерями. Но пока что мы ничего не потеряли. У нас есть только блоки 8х8 YUV-компонентов, преобразованные в блоки 8х8 косинусных функций без потери информации. Этап потери данных — это квантование.

Квантованием называют процесс, когда мы берём два значения из определённого диапазона и превращаем их в дискретное значение. В нашем случае это лишь хитрое название для сведения к 0 самых высокочастотных коэффициентов в получившейся ДКП-матрице. При сохранении изображения с помощью JPEG большинство графических редакторов спрашивают, какой уровень сжатия вы хотите задать. Здесь и происходит потеря высокочастотной информации. Вы уже не сможете воссоздать исходную картинку из получившегося JPEG-изображения.

В зависимости от степени сжатия используются разные матрицы квантования (забавный факт: большинство разработчиков имеют патенты на создание таблицы квантования). Мы поэлементно делим ДКП-матрицу коэффициентов на матрицу квантования, округляем результаты до целых чисел и получаем квантованную матрицу.

Рассмотрим пример. Допустим, есть такая ДКП-матрица:

А вот обычная матрица квантования:

Квантованная матрица будет выглядеть так:

Хотя человек и не видит высокочастотную информацию, если вы удалите слишком много данных из блоков 8х8 пикселей, изображение станет выглядеть слишком грубо. В такой квантованной матрице самое первое значение называется DC-значением, а все остальные — AC-значениями. Если бы мы взяли DC-значения всех квантованных матриц и сгенерировали новую картинку, то получили бы превьюшку с разрешением в 8 раз меньше исходного изображения.

Также хочу отметить, что поскольку мы применяли квантование, нужно убедиться, что цвета попадают в диапазон [0,255]. Если они из него вылетают, то придётся вручную привести их к этому диапазону.

Зигзаг

После квантования алгоритм JPEG использует зигзаг-сканирование для преобразования матрицы к одномерному виду:

Пусть у нас такая квантованная матрица:

Тогда результат зигзаг-сканирование будет таким:

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

Кодирование длин серий и дельта-кодирование

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

После кодирования длин серий получим вот что:

Мы сжали 7 байтов в 2 байта.

Дельта-кодирование используется для представления байта относительно байта до него. Проще будет объяснить на примере. Пусть у нас такие данные:

С помощью дельта-кодирования их можно представить так:

В JPEG каждое DC-значение ДКП-матрицы задаётся с помощью дельта-кодирования относительно предыдущего DC-значения. Это означает, что изменив самый первый ДКП-коэффициент изображения вы порушите всю картинку. Но если если изменить первое значение последней ДКП-матрицы, то это повлияет лишь на очень маленький фрагмент изображения.

Такой подход полезен, потому что первое DC-значение изображения обычно варьируется сильнее всего, и с помощью дельта-кодирования мы приводим остальные DC-значения ближе к 0, что улучшает сжатие с помощью кодирования Хаффмана.

Кодирование Хаффмана

Это метод сжатия информации без потерь. Однажды Хаффман задался вопросом: «Какое наименьшее количество битов я могу использовать для хранения произвольного текста?». В результате был создан формат кодирования. Допустим, у нас есть текст:

Обычно каждый символ занимает один байт пространства:

Это принцип двоичной кодировки ASCII. А если нам изменить сопоставление?

Теперь нам нужно гораздо меньше битов для хранения того же текста:

Всё это хорошо, но что если нам нужно ещё сильнее сэкономить пространство? Например, так:

Кодирование Хаффмана позволяет использовать такое сопоставление переменной длины. Берутся входные данные, чаще всего встречающиеся символы сопоставляются с более маленьким сочетанием битов, а менее частые символы — с более крупными сочетаниями. А затем получившееся сопоставления собираются в двоичное дерево. В JPEG мы сохраняем ДКП-информацию с помощью кодирования Хаффмана. Помните, я упоминал, что дельта-кодирование DC-значений облегчает кодирование Хаффмана? Надеюсь, вы теперь понимаете, почему. После дельта-кодирования нам нужно сопоставить меньше «символов» и размер дерева уменьшается.

У Тома Скотта есть прекрасное видео, поясняющее работу алгоритма Хаффмана. Посмотрите, прежде чем читать дальше.

В JPEG содержится до четырёх таблиц Хаффмана, которые хранятся в разделе «Define Huffman Table» (начинается с 0xffc4 ). ДКП-коэффициенты хранятся в двух разных таблицах Хаффмана: в одной DC-значения из зигзаг-таблиц, в другой — АС-значения из зигзаг-таблиц. Это означает, что при кодировании нам нужно объединить DC- и АС-значения из двух матриц. ДКП-информация для каналов яркости и хроматичности хранится отдельно, так что у нас два набора DC- и два набора AC-информации, в сумме 4 таблицы Хаффмана.

Если изображение представлено в оттенках серого, то у нас только две таблицы Хаффмана (одна для DC и одна для AC), потому что нам не нужен цвет. Как вы уже могли понять, два разных изображения могут иметь очень разные таблицы Хаффмана, поэтому важно хранить их внутри каждого JPEG.

Теперь мы знаем основное содержимое JPEG-изображений. Переходим к декодированию.

Декодирование JPEG

Декодирование можно разделить на этапы:

kbozmoQUZvyztsUX9Kpip3UqccmMoVtYL5R94X xk 3ThiF I2mLEWGryfWjPg

Извлечение таблиц Хаффмана

Мы уже знаем, что JPEG содержит четыре таблицы Хаффмана. Это последний кодирования, поэтому декодировать мы начнём с него. В каждом разделе с таблицей содержится информация:

После вызова GetHuffmanBits список root будет содержать такие данные:

Декодирование таблицы квантования

Раздел Define Quantization Table содержит такие данные:

Поле Размер Описание
Идентификатор маркера 2 байта 0xff и 0xdb идентифицируют раздел DQT
Длина 2 байта Длина таблицы квантования
Информация о квантовании 1 байт биты 0. 3: количество таблиц квантования (0. 3, иначе ошибка) биты 4. 7: точность таблицы квантования, 0 = 8 битов, иначе 16 битов
Байты n байтов Значения таблицы квантования, n = 64*(точность+1)

Если вывести матрицы квантования для нашей картинки, то получим вот что:

Декодирование начала кадра

Раздел Start of Frame содержит такую информацию (источник):

Поле Размер Описание
Идентификатор маркера 2 байта 0xff и 0xc0 для идентификации маркера SOF
Длина 2 байта Значение эквивалентно формуле 8 + компоненты*3
Точность данных 1 байт Это в битах в образце, обычно равно 8 (12 и 16 не поддерживаются большинством приложений).
Высота изображения 2 байта Должно быть > 0
Ширина изображения 2 байта Должно быть > 0
Количество компонентов 1 байт Обычно 1 = оттенки серого, 3 = YcbCr или YIQ
Каждый компонент 3 байта Считывает каждый компонент данных по 3 байта. Он содержит идентификатор компонента (1 байт) (1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q), факторы выборки (1 байт) (биты 0. 3 по вертикали, 4. 7 по горизонтали), количество таблиц квантования (1 байт).

Здесь нам интересно не всё. Мы извлечём ширину и высоту картинки, а также количество таблиц квантования для каждого компонента. Ширину и высоту будем использовать для начала декодирования фактических сканов изображения из раздела Start of Scan. Поскольку мы будем работать по большей части с YCbCr-изображением, то можно предположить, что компонентов будет три, а их типы будут 1, 2 и 3 соответственно. Напишем код для декодирования этих данных:

Декодирование Start of Scan

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

Как вы помните, JPEG разбивает изображение на матрицы 8х8. Теперь нам нужно преобразовать данные скана изображения в битовый поток и обработать его по фрагментам 8х8. Добавим код в наш класс:

BuildMatrix возьмёт таблицу квантования и добавит параметры, создаст матрицу обратного дискретного косинусного преобразования и даст нам матрицы Y, Cr и Cb. А функция DrawMatrix преобразует их в RGB.

Если вы модифицируете зигзаг-таблицу в подобный вид:

то получите такой результат (обратите внимание на маленькие артефакты):

А если наберётесь храбрости, то можете ещё больше модифицировать зигзаг-таблицу:

Тогда результат будет таким:

Завершим наш метод BuildMatrix :

Мы начинаем с создания класса инвертирования дискретной косинусной трансформации ( IDCT() ). Затем считываем данные в битовый поток и декодируем с помощью таблицы Хаффмана.

self.huffman_tables[0] и self.huffman_tables[1] ссылаются на DC-таблицы для яркости и хроматичности соответственно, а self.huffman_tables[16] и self.huffman_tables[17] ссылаются на AC-таблицы для яркости и хроматичности соответственно.

Затем повторим ту же процедуру декодирования с AC-значениями в матрице квантования. Значение кода 0 говорит о том, что мы дошли до маркера окончания блока (End of Block, EOB) и должны остановиться. Более того, первая часть АС-таблицы квантования говорит нам, сколько у нас начальных нулей. Теперь вспоминаем про кодирование длин серий. Обратим этот процесс вспять и пропустим все эти многочисленные биты. В классе IDCT им явно присваиваются нули.

Метод BuildMatrix вернёт инвертированную ДКП-матрицу и значение DC-коэффициента. Помните, что это будет матрица только для одной минимальной единицы кодирования размером 8×8. Проделаем это для всех остальных MCU нашего файла.

Вывод изображения на экран

Теперь сделаем так, чтобы наш код в методе StartOfScan создавал Tkinter Canvas и рисовал каждый MCU после декодирования.

Заключение

Кто бы мог подумать, что для показа моего лица придётся написать объяснение более чем 6000 слов. Удивительно, насколько умны были авторы некоторых алгоритмов! Надеюсь, вам понравилась статья. Я очень многому научился, пока писал этот декодер. Не думал, что в кодировании простого JPEG-изображения используется столько математики. В следующий раз можете попробовать написать декодер для PNG (или другого формата).

Дополнительные материалы

Если вас интересуют подробности, почитайте материалы, которые я использовал при написании статьи, а также кое-какие дополнительные работы:

Источник

Общеобразовательный справочник
Adblock
detector