Friday, March 30, 2012

Thursday, March 29, 2012

Декодирование JPEG для чайников http://habrahabr.ru/post/102521/

http://habrahabr.ru/post/102521/

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

[FF D8]

Вам когда-нибудь хотелось узнать как устроен jpg-файл? Сейчас разберемся! Прогревайте ваш любимый компилятор и hex-редактор, будем декодировать это:

Специально взял рисунок поменьше. Это знакомый, но сильно пережатый favicon Гугла: 

Сразу предупреждаю, что описание упрощено, и приведенная информация не полная, но зато потом будет легко понять спецификацию.

Даже не зная, как происходит кодирование, мы уже можем кое-что извлечь из файла.
[FF D8] — маркер начала. Он всегда находится в начале всех jpg-файлов.
Следом идут байты [FF FE]. Это маркер, означающий начало секции с комментарием. Следующие 2 байта [00 04] — длина секции (включая эти 2 байта). Значит в следующих двух [3A 29] — сам комментарий. Это коды символов ":" и ")", т.е. обычного смайлика. Вы можете увидеть его в первой строке правой части hex-редактора.

Немного теории


Очень кратко по шагам:
  1. Обычно изображение преобразуется из цветового пространства RGB в YCbCr.
  2. Часто каналы Cb и Cr прореживают, то есть блоку пикселей присваивается усредненное значение. Например, после прореживания в 2 раза по вертикали и горизонтали, пиксели будут иметь такое соответствие:
  3. Затем значения каналов разбиваются на блоки 8x8 (все видели эти квадратики на слишком сжатом изображении).
  4. Каждый блок подвергается дискретно-косинусному-преобразованию (ДКП), являющемся разновидностью дискретного преобразования Фурье. Получим матрицу коэффициетов 8x8. Причем левый верхний коэффициент называется DC-коффициентом (он самый важный и является усредненным значением всех значений), а оставшиеся 63 — AC-коэффициентами.
  5. Получившиеся коэффициенты квантуются, т.е. каждый умножается на коэффициент матрицы квантования (каждый кодировщик обычно использует свою матрицу квантования).
  6. Затем они кодируются кодами Хаффмана.
Давайте подумаем, в каком порядке могут быть закодированы эти данные. Допустим, сначала полностью, для всего изображения, закодирован канал Y, затем Cb, потом Cr. Все помнят загрузку картинок на диал-апе. Если бы они кодировались именно так, нам бы пришлось ждать загрузки всего изображения, прежде чем оно появится на экране. Так же будет неприятно, если потерятся конец файла. Вероятно, существуют и другие весомые причины. Поэтому закодированные данные располагаются поочередно, небольшими частями.



Напоминаю, что каждый блок Yij, Cbij, Crij — это матрица коэффициентов ДКП, закодированная кодами Хаффмана. В файле они располагаются в таком порядке: Y00Y10Y01Y11Cb00Cr00Y20

Чтение файла


После того, как мы извлекли комментарий, будет легко понять, что:
  • Файл поделен на секторы, предваряемые маркерами.
  • Маркеры имеют длину 2 байта, причем первый байт [FF].
  • Почти все секторы хранят свою длину в следующих 2 байта после маркера.
Для удобства подсветим маркеры:
FF D8 FF FE 00 04 3A 29 FF DB 00 43 00 A0 6E 78
8C 78 64 A0 8C 82 8C B4 AA A0 BE F0 FF FF F0 DC
DC F0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF DB 00
43 01 AA B4 B4 F0 D2 F0 FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF C0 00 11 08 00 10 00 10 03 01 22 00 02
11 01 03 11 01 FF C4 00 15 00 01 01 00 00 00 00
00 00 00 00 00 00 00 00 00 00 03 02 FF C4 00 1A
10 01 00 02 03 01 00 00 00 00 00 00 00 00 00 00
00 01 00 12 02 11 31 21 FF C4 00 15 01 01 01 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 FF
C4
00 16 11 01 01 01 00 00 00 00 00 00 00 00 00
00 00 00 00 11 00 01 FF DA 00 0C 03 01 00 02 11
03 11 00 3F 00 AE E7 61 F2 1B D5 22 85 5D 04 3C
82 C8 48 B1 DC BF FF D9


Маркер [FF DB]: DQT — таблица квантования.


                        FF DB 00 43 00 A0 6E 78
8C 78 64 A0 8C 82 8C B4 AA A0 BE F0 FF FF F0 DC
DC F0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF

Заголовок секции всегда занимает 3 байта. В нашем случае это [00 43 00]. Заголовок состоит из:
[00 43]  Длина: 0x43 = 67 байт
[0_]     Длина значений в таблице: 0 (0 — 1 байт, 1 — 2 байта)
[_0]     Идентификатор таблицы: 0
Оставшимися 64-мя байтами нужно заполнить таблицу 8x8.
 [A0 6E 64 A0 F0 FF FF FF] [78 78 8C BE FF FF FF FF] [8C 82 A0 F0 FF FF FF FF] [8C AA DC FF FF FF FF FF] [B4 DC FF FF FF FF FF FF] [F0 FF FF FF FF FF FF FF] [FF FF FF FF FF FF FF FF] [FF FF FF FF FF FF FF FF] 

Приглядитесь, в каком порядке заполнены значения таблицы. Этот порядок называется zigzag order: 


Маркер [FF C0]: SOF0 — Baseline DCT


Этот маркер называется SOF0, и означает, что изображение закодировано базовым методом. Он очень распространен. Но в интернете не менее популярен знакомый вам progressive-метод, когда сначала загружается изображение с низким разрешением, а потом и нормальная картинка. Это позволяет понять что там изображено, не дожидаясь полной загрузки. Спецификация определяет еще несколько, как мне кажется, не очень распространенных методов.

      FF C0 00 11 08 00 10 00 10 03 01 22 00 02
11 01 03 11 01


[00 11]  Длина: 17 байт. 
[08]     Precision: 8 бит. В базовом методе всегда 8. Как я понял, это разрядность значений каналов. 
[00 10]  Высота рисунка: 0x10 = 16
[00 10]  Ширина рисунка: 0x10 = 16
[03]     Количество компонентов: 3. Чаще всего это Y, Cb, Cr.

1-й компонент:
[01]  Идентификатор: 1
[2_]  Горизонтальное прореживание (H1): 2
[_2]  Вертикальное прореживание (V1): 2
[00]  Идентификатор таблицы квантования: 0

2-й компонент:
[02]  Идентификатор: 2
[1_]  Горизонтальное прореживание (H2): 1
[_1]  Вертикальное прореживание (V2): 1
[01]  Идентификатор таблицы квантования: 1

3-й компонент:
[03]  Идентификатор: 3
[1_]  Горизонтальное прореживание (H3): 1
[_1]  Вертикальное прореживание (V3): 1
[01]  Идентификатор таблицы квантования: 1

Теперь посмотрите, как определить насколько прорежено изображение. Находим Hmax=2 и Vmax=2. Канал i будет прорежен в Hmax/Hi раз по горизонтали и Vmax/Vi раз по вертикали. 

Маркер [FF C4]: DHT (таблица Хаффмана)


Эта секция хранит коды и значения полученные кодированием Хаффмана.

               FF C4 00 15 00 01 01 00 00 00 00
00 00 00 00 00 00 00 00 00 00 03 02


[00 15]  длина: 21 байт. 
[0_]     класс: 0 (0 — таблица DC коэффициэнтов, 1 — таблица AC коэффициэнтов).
[_0]     идентификатор таблицы: 0
Длина кода Хаффмана: 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
Количество кодов:  [01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00]
Количество кодов означает количество кодов такой длины. Обратите внимание, что секция хранит только длины кодов, а не сами коды. Мы должны найти коды сами. Итак, у нас есть один код длины 1 и один — длины 2. Итого 2 кода, больше кодов в этой таблице нет.
С каждым кодом сопоставлено значение, в файле они перечислены следом. Значения однобайтовые, поэтому читаем 2 байта. 
[03] — значение 1-го кода.
[02] — значение 2-го кода.

Далее в файле можно видеть еще 3 маркера [FF C4], я пропущу разбор соответствующих секций, он аналогичен вышеприведенному.

Построение дерева кодов Хаффмана


Мы должны построить бинарное дерево по таблице, которую мы получили в секции DHT. А уже по этому дереву мы узнаем каждый код. Значения добавляем в том порядке, в каком указаны в таблице. Алгоритм прост: в каком бы узле мы ни находились, всегда пытаемся добавить значение в левую ветвь. А если она занята, то в правую. А если и там нет места, то возвращаемся на уровень выше, и пробуем оттуда. Остановиться нужно на уровне равном длине кода. Левым ветвям соответствует значение 0, правым — 1.
Замечание:
Не нужно каждый раз начинать с вершины. Добавили значение — вернитесь на уровень выше. Правая ветвь существует? Если да, идите опять вверх. Если нет — создайте правую ветвь и перейдите туда. Затем, с этого места, начинайте поиск для добавления следующего значения.


Деревья для всех таблиц этого примера:

UPD (спасибо anarsoul): В узлах первого дерева (DC, id =0) должны быть значения 0x03 и 0x02

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

Маркер [FF DA]: SOS (Start of Scan)


Байт [DA] в маркере означает — «ДА! Наконец-то то мы перешли непосредственно к разбору секции закодированного изображения!». Однако секция символично называется SOS. 

                     FF DA 00 0C 03 01 00 02 11
03 11 00 3F 00


[00 0C]  Длина заголовочной части (а не всей секции): 12 байт. 
[03]     Количество компонентов сканирования. У нас 3, по одному на Y, Cb, Cr. 

1-й компонент:
[01]  Номер компонента изображения: 1 (Y)
[0_]  Идентификатор таблицы Хаффмана для DC коэффициэнтов: 0
[_0]  Идентификатор таблицы Хаффмана для AC коэффициэнтов: 0

2-й компонент:
[02]  Номер компонента изображения: 2 (Cb)
[1_]  Идентификатор таблицы Хаффмана для DC коэффициэнтов: 1
[_1]  Идентификатор таблицы Хаффмана для AC коэффициэнтов: 1

3-й компонент:
[03]  Номер компонента изображения: 3 (Cr)
[1_]  Идентификатор таблицы Хаффмана для DC коэффициэнтов: 1
[_1]  Идентификатор таблицы Хаффмана для AC коэффициэнтов: 1

Данные компоненты циклически чередуются.

[00], [3F], [00] Об этих байтах можно почитать в спецификации. 

На этом заголовочная часть заканчивается, отсюда и до конца (маркера [FF D9]) закодированные данные.

[AE]    [E7]    [61]    [F2]    [1B]
101011101110011101100001111100100


Нахождение DC-коэффициента.
1. Читаем последовательность битов (если встретим 2 байта [FF 00], то это не маркер, а просто байт [FF]). После каждого бита сдвигаемся по дереву Хаффмана (с соответствующим идентификатором) по ветви 0 или 1, в зависимости от прочитанного бита. Останавливаемся, если оказались в конечном узле. 
101011101110011101100001111100100

2. Берем значение узла. Если оно равно 0, то коэффициент равен 0, записываем в таблицу и переходим к чтению других коэффициентов. В нашем случае — 02. Это значение — длина коэффициента в битах. Т. е. читаем следующие 2 бита, это и будет коэффициент.
101011101110011101100001111100100

3. Если первая цифра значения в двоичном представлении — 1, то оставляем как есть: DC_coef = значение. Иначе преобразуем: DC_coef = значение-2длина значения+1. Записываем коэффициент в таблицу в начало зигзага — левый верхний угол.

Нахождение AC-коэффициентов.
1. Аналогичен п. 1, нахождения DC коэффициента. Продолжаем читать последовательность:
101011101110011101100001111100100

2. Берем значение узла. Если оно равно 0, это означает, что оставшиеся значения матрицы нужно заполнить нулями. Дальше закодирована уже следующая матрица. Первые несколько дочитавших до этого места и написавших об этом мне в личку, получат плюс в карму. В нашем случае значение узла: 0x31.
Первый полубайт: 0x3 — именно столько нулей мы должны добавить в матрицу. Это 3 нулевых коэффициэнта.
Второй полубайт: 0x1 — длина коэффициэнта в битах. Читаем следующий бит.
101011101110011101100001111100100

3. Аналогичен п. 3 нахождения DC-коэффициента.

Как вы уже поняли, читать AC-коэффициенты нужно пока не наткнемся на нулевое значение кода, либо пока не заполнится матрица.
В нашем случае мы получим:
101011101110011101100001111100100
и матрицу:
 [2  0  3 0 0 0 0 0] [0  1  2 0 0 0 0 0] [0 -1 -1 0 0 0 0 0] [1  0  0 0 0 0 0 0] [0  0  0 0 0 0 0 0] [0  0  0 0 0 0 0 0] [0  0  0 0 0 0 0 0] [0  0  0 0 0 0 0 0] 

Вы заметили, что значения заполнены в том же зигзагообразном порядке?
Причина использования такого порядка простая — так как чем больше значения v и u, тем меньшей значимостью обладает коэффициент Svu в дискретно-косинусном преобразовании. Поэтому, при высоких степенях сжатия малозначащие коэффициенты обнуляют, тем самым уменьшая размер файла.

Аналогично получаем еще 3 матрицы Y-канала…
 [-4  1 1 1 0 0 0 0]  [ 5 -1  1 0 0 0 0 0] [ 0  0 1 0 0 0 0 0]  [-1 -2 -1 0 0 0 0 0]  [ 0 -1 0 0 0 0 0 0]  [ 0 -1  0 0 0 0 0 0]   [ 0  0 0 0 0 0 0 0]  [-1  0  0 0 0 0 0 0]   [ 0  0 0 0 0 0 0 0]  [ 0  0  0 0 0 0 0 0]   [ 0  0 0 0 0 0 0 0]  [ 0  0  0 0 0 0 0 0]  [ 0  0 0 0 0 0 0 0]  [ 0  0  0 0 0 0 0 0]   [ 0  0 0 0 0 0 0 0]  [ 0  0  0 0 0 0 0 0]   [-4  2  2 1 0 0 0 0] [-1  0 -1 0 0 0 0 0] [-1 -1  0 0 0 0 0 0] [ 0  0  0 0 0 0 0 0] [ 0  0  0 0 0 0 0 0] [ 0  0  0 0 0 0 0 0] [ 0  0  0 0 0 0 0 0] [ 0  0  0 0 0 0 0 0] 

Ой, я забыл сказать, что закодированные DC-коэффициенты — это не сами DC-коэффициенты, а их разности между коэффициентами предыдущей таблицы (того же канала)! Нужно поправить матрицы:
DC для 2-ой: 2 + (-4) = -2
DC для 3-ой: -2 + 5 = 3
DC для 4-ой: 3 + (-4) = -1

 [-2  1 1 1 0 0 0 0]  [ 3 -1  1 0 0 0 0 0]  [-1  2  2 1 0 0 0 0]     ..........           ..........             .......... 

Теперь порядок. Это правило действует до конца файла. 

… и по матрице для Cb и Cr:
 [-1 0 0 0 0 0 0 0]  [0  0 0 0 0 0 0 0]  [ 1 1 0 0 0 0 0 0]  [1 -1 0 0 0 0 0 0]  [ 0 0 0 0 0 0 0 0]  [1  0 0 0 0 0 0 0]  [ 0 0 0 0 0 0 0 0]  [0  0 0 0 0 0 0 0]  [ 0 0 0 0 0 0 0 0]  [0  0 0 0 0 0 0 0]  [ 0 0 0 0 0 0 0 0]  [0  0 0 0 0 0 0 0]  [ 0 0 0 0 0 0 0 0]  [0  0 0 0 0 0 0 0]   [ 0 0 0 0 0 0 0 0]  [0  0 0 0 0 0 0 0]  

Так как тут только по одной матрице, DC-коэфициенты можно не трогать.

Вычисления


Квантование


Вы помните, что матрица проходит этап квантования? Элементы матрицы нужно почленно перемножить с элементами матрицы квантования. Осталось выбрать нужную. Сначала мы просканировали первый компонент, его компонента изображения = 1. Компонент изображения с таким идентификатором использует матрицу квантования 0 (у нас она первая из двух). Итак, после перемножения:
 [320     0  300  0  0  0  0  0] [  0   120  280  0  0  0  0  0] [  0  -130 -160  0  0  0  0  0] [140     0    0  0  0  0  0  0] [  0     0    0  0  0  0  0  0] [  0     0    0  0  0  0  0  0] [  0     0    0  0  0  0  0  0] [  0     0    0  0  0  0  0  0] 

Аналогично получаем еще 3 матрицы Y-канала…
 [-320   110  100  160 0 0 0 0]  [ 480  -110   100 0 0 0 0 0]  [   0     0  140    0 0 0 0 0]  [-120  -240  -140 0 0 0 0 0] [   0  -130    0    0 0 0 0 0]  [   0  -130     0 0 0 0 0 0]  [   0     0    0    0 0 0 0 0]  [-140     0     0 0 0 0 0 0]  [   0     0    0    0 0 0 0 0]  [   0     0     0 0 0 0 0 0] [   0     0    0    0 0 0 0 0]  [   0     0     0 0 0 0 0 0] [   0     0    0    0 0 0 0 0]  [   0     0     0 0 0 0 0 0] [   0     0    0    0 0 0 0 0]  [   0     0     0 0 0 0 0 0]  [-160   220   200  160 0 0 0 0] [-120     0  -140    0 0 0 0 0] [-140  -130     0    0 0 0 0 0] [   0     0     0    0 0 0 0 0] [   0     0     0    0 0 0 0 0] [   0     0     0    0 0 0 0 0] [   0     0     0    0 0 0 0 0] [   0     0     0    0 0 0 0 0] 

… и по матрице для Cb и Cr.
 [-170    0 0 0 0 0 0 0]  [  0     0 0 0 0 0 0 0] [ 180  210 0 0 0 0 0 0]  [180  -210 0 0 0 0 0 0] [   0    0 0 0 0 0 0 0]  [240     0 0 0 0 0 0 0] [   0    0 0 0 0 0 0 0]  [  0     0 0 0 0 0 0 0] [   0    0 0 0 0 0 0 0]  [  0     0 0 0 0 0 0 0] [   0    0 0 0 0 0 0 0]  [  0     0 0 0 0 0 0 0] [   0    0 0 0 0 0 0 0]  [  0     0 0 0 0 0 0 0] [   0    0 0 0 0 0 0 0]  [  0     0 0 0 0 0 0 0] 

Обратное дискретно-косинусное преобразование






Формула не должна доставить сложностей*. Svu — наша полученная матрица коэффициентов. u — столбец, v — строка. syx — непосредственно значения каналов.

*Вообще говоря, это не совсем правда. Когда я смог декодировать и отобразить на экране рисунок 16x16, я взял изображение размером 600x600 (кстати, это была обложка любимого альбома Mind.In.A.Box — Lost Alone). Получилось не сразу — всплыли различные баги. Вскоре я мог любоваться корректно загруженной картинкой. Только очень огорчала скорость загрузки. До сих пор помню, она занимала 7 секунд. Но это и неудивительно, если бездумно пользоваться приведенной формулой, то для вычисления одного канала одного пикселя потребуется нахождения 128 косинусов, 768 умножений, и сколько-то там сложений. Только вдумайтесь — почти тысяча непростых операций только на один канал одного пиксела! К счастью, тут есть простор для отимизации (после долгих экспериментов уменьшил время загрузки до предела точности таймера 15мс, и после этого сменил изображение на фотографию в 25 раз большей площадью. Возможно, напишу об этом отдельной статьей).

Напишу результат вычисления только первой матрицы канала Y (значения округлены):
 [138  92 27 -17 -17  28 93 139] [136  82  5 -51 -55  -8 61 111] [143  80 -9 -77 -89 -41 32  86] [157  95  6 -62 -76 -33 36  86] [147 103 37 -12 -21  11 62 100] [ 87  72 50  36  37  55 79  95] [-10   5 31  56  71  73 68  62] [-87 -50  6  56  79  72 48  29] 

и 2-х оставшихся:
 Cb                                  Cr [  60  52  38  20   0 -18 -32 -40]  [ 19  27  41  60  80  99 113 120] [  48  41  29  13  -3 -19 -31 -37]  [  0   6  18  34  51  66  78  85] [  25  20  12   2  -9 -19 -27 -32]  [-27 -22 -14  -4   7  17  25  30] [  -4 -6   -9 -13 -17 -20 -23 -25]  [-43 -41 -38 -34 -30 -27 -24 -22] [ -37 -35 -33 -29 -25 -21 -18 -17]  [-35 -36 -39 -43 -47 -51 -53 -55] [ -67 -63 -55 -44 -33 -22 -14 -10]  [ -5  -9 -17 -28 -39 -50 -58 -62] [ -90 -84 -71 -56 -39 -23 -11  -4]  [ 32  26  14  -1 -18 -34 -46 -53] [-102 -95 -81 -62 -42 -23  -9  -1]  [ 58  50  36  18  -2 -20 -34 -42] 

А теперь… мини-тест!
Что делать дальше?
  1. О, пойду-ка поем!
  2. Да я вообще не въезжаю, о чем речь.
  3. Раз значение цветов YCbCr получены, осталось преобразовать в RGB, типа так:YCbCrToRGB(Yij, Cbij, Crij), Yij, Cbij, Crij — наши полученные матрицы.
  4. 4 матрицы Y, и по одной Cb и Cr, так как мы прореживали каналы и 4 пикселям Y соответствует по одному Cb и Cr. Поэтому вычислять так: YCbCrToRGB(Yij, Cb[i/2][j/2], Cr[i/2][j/2])
Если вы выбрали 1 и 4, то я рад за вас. Либо вы все правильно поняли, либо скоро будете получать удовольствие от еды. 

YCbCr в RGB


R = Y + 1.402 * Cr
G = Y - 0.34414 * Cb - 0.71414 * Cr
B = Y + 1.772 * Cb

Не забудьте прибавить по 128. Если значения выйдут за пределы интервала [0, 255], то присвоить граничные значения. Формула простая, но тоже отжирает долю процессорного времени.

Вот полученные таблицы для каналов R, G, B для левого верхнего квадрата 8x8 нашего примера: 
 [255 255 182 149 138 194 248 255] [255 218 133  85  73 128 189 247] [255 246 146  89  66 125 187 252] [255 231 134  74  52 103 164 222] [255 255 192 154 134 177 217 255] [215 208 178 172 165 191 207 231] [145 171 186 222 226 239 223 228] [ 41  86 134 192 207 208 176 165]  [232 183 121  74  77 119 187 230] [247 192 116  59  56 102 172 221] [237 171  85  14   5  50 126 177] [255 205 117  48  35  77 147 196] [241 194 131  79  73 102 156 191] [198 182 161 146 148 165 190 205] [ 84  96 125 147 165 164 162 153] [ 24  60 117 166 190 182 159 139]  [255 255 255 203 217 248 255 255] [255 255 218 150 158 193 255 255] [255 255 225 143 145 179 255 255] [255 255 219 139 137 168 249 255] [255 255 255 208 213 231 255 255] [255 255 255 237 250 255 255 255] [224 225 255 255 255 255 255 255] [126 151 219 255 255 255 255 230]

Осталось вывести изображение на экран.

Конец


Вообще я не специалист по JPEG, поэтому вряд ли смогу ответить на все вопросы. Просто когда я писал свой декодер, мне часто приходилось сталкиваться с различными непонятными проблемами. И когда изображение выводилось некорректно, я не знал где допустил ошибку. Может неправильно проинтерпретировал биты, а может неправильно использовал ДКП. Очень не хватало пошагового примера, поэтому, надеюсь, эта статья поможет при написании декодера. Думаю, она покрывает описание базового метода, но все-равно нельзя обойтись только ей. Предлагаю вам ссылки, которые помогли мне:
ru.wikipedia.org/JPEG — для поверхностного ознакомления.
en.wikipedia.org/JPEG — гораздо более толковая статья о процессах кодирования/декодирования.
JPEG Standard (JPEG ISO/IEC 10918-1 ITU-T Recommendation T.81) — не обойтись без 186-страничной спецификации. Но нет повода для паники — три четверти занимают блок-схемы и приложения. 
impulseadventure.com/photo — Хорошие подробные статьи. По примерам я разобрался как строить деревья Хаффмана и использовать их при чтении соответствующей секции. 
JPEGsnoop — На том же сайте есть отличная утилита, которая вытаскивает всю информацию jpeg-файла.

[FF D9]
+402
31 августа 2010, 19:23
620
Fil 115,7

комментарии (134)

+16
vectorg31 августа 2010, 19:39#
это прекрасно! очень давно искал подобную статью, кратко и понятно.
+31
Fil31 августа 2010, 19:42#
Спасибо! Значит я не зря старался :)
+2
Colwin1 сентября 2010, 06:42#
Только недавно пытался въехать в спецификацию, со скрипом — и вот тебе готовое, разжёванное решение =) (ну, не совсем, конечно, но с ним намного проще).
Автор — молодец!
+15
ehvadimka31 августа 2010, 19:42#
Вот что надо деткам в школе в 7 классе давать на информатике, а не устройство байта :)
+10
xspander31 августа 2010, 19:49#
«Обратное дискретно-косинусное преобразование»
Такое в школе вроде не изучают…
+2
Lifz31 августа 2010, 19:49#
Ну не в 7 классе, чуть позже. Но все таки стоит!
+1
w999d31 августа 2010, 19:53#
чем раньше, тем лучше, меньше вопросов будет потом
+2
Lifz31 августа 2010, 19:56#
Далеко не факт, скорее будет куча глупых вопросов.
0
Colwin1 сентября 2010, 06:44#
Не меньше ) если нет базы, то все эти ценнейшие знания пройдут мимо.
А базу, как показывает практика, нарабатывают только те, кто сам задается целью изучить устройство компа и программирование. А для остальных все это будет мусором, увы )
+10
Coderr31 августа 2010, 19:51#
не буду утверждать, что так во всех школах, но, приходилось сидеть на уроке информатики в 5 классе, в школе. Детей спрашивают, что такое монитор, они отвечают контакт. Детей спрашивают, что такое интернет — дети отвечают контакт и Мой мир@mail.ru. Детям пытаются объяснить базовые принципы устройства компьютера, то же устройство байта, etc., дети ничего не слушают, им бы быстрее «вконтактик»…
Сейчас образование не на самом высшем уровне, и информатика — не исключение.
0
vk231 августа 2010, 20:40#
ровно так же 10 лет назад школьники не понимали, как работает микроволновка, а 20 лет назад — телевизор.

это нормально, компьютеры должны упрощаться. Если у меня в руках iPad — какой нафиг «монитор» и «системный блок»? Если у меня машина с АКПП — так ли мне необходимо знать, как работает планетарная передача?

не вижу смысла преподавать информатику в том виде, в каком ее, судя по всему, преподают.
+1
infi31 августа 2010, 22:24#
Да, и математику нафиг, у меня же калькулятор.
0
vk231 августа 2010, 23:06#
в каком-то смысле — да. 

для нематематиков есть калькулятор.

я вот не уверен, что мы с Вами (очевидно, оба имеющие отношение к IT-области) что-то можем сейчас рассказать о валентности или пестиках-тычинках; представляю, кстати, как смеются над нами химики на своем химическом хабрахабре… и о наших представлениях о том, как размножаются бабочки.

да что говорить. я вот даже специально shift зажму.

УСТНЫЙ СЧЕТ ДАВНО СТАЛ РЕДКОСТЬЮ! 

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

Вы думаете, я про новое поколение? да нет, про тридцатилетних тоже. Кто из них помнит правило «умножения на 11», например?

нет, я уверен, в Вашем окружении — все помнят, но если взять чуть более общую картину?
0
b8c61 сентября 2010, 11:24#
Да ладно вам умножение, расплачиваетесь — проверяйте сдачу. И тренировка, и простые алгоритмы будете в голове держать.

Это как отжиматься — вроде само по себе оно не нужно, а не такой развалиной себя чувствуешь.
+1
ostapbender1 сентября 2010, 11:35#
Пластик?
0
b8c62 сентября 2010, 14:08#
На пластике надо проверять особенно :-)
0
vk21 сентября 2010, 14:21#
Я-то считать в ума с детства люблю и считаю естественным. Но, как показала жизнь, таких меньшинство.
0
b8c62 сентября 2010, 14:20#
С другой стороны это даже здорово. Конкуренцию легче составлять, так сказать…
+1
IraRio31 августа 2010, 23:31#
честно говоря большинство моих преподов арифметические ошибки ошибками не считали, и ничего против калькуляторов не имели.
0
Colwin1 сентября 2010, 06:46#
Какое упущение с их стороны ) арифметика и ее закономерности — одна из самых интересных областей математики в начальной школе.
0
P0figist1 сентября 2010, 11:53#
Интересно — понятия слишком субъективное, чтобы судить по нему об опрометчивости преподавателей.
Да и вообще, как по мне, в школе инетесно все, что понятно. А понятно становится когда тебе понятно объясняют. Так что: не могут заинтересовать, пусть хоть не насилуют.
+1
mono2k31 августа 2010, 23:44#
арифметику.
+1
kurokikaze1 сентября 2010, 13:01#
Как же вы узнаете что именно нажимать на калькуляторе? :)
+2
wapoo31 августа 2010, 22:28#
Вы хотите сказать, что сейчас школьники понимают, как работает телевизор или микроволновка, и что эти устройства упростились? Если не объяснять основы, как «устройство байта» и прочее, то такая «информатика» даром не нужна.
–2
vk231 августа 2010, 23:00#
нет, и сейчас не понимают. да, конечно, устройства упростились до «нажми кнопку и оно будет работать». да, я считаю, что знать принцип работы холодильника не обязательно для счастья в жизни (господи, да кто из Ваших знакомых сможет его объяснить?).

другое дело, что многие вещи в школе преподаются для того, чтобы ребенок мог понадкусывать побольше яблок (как в анекдоте) и понять, что ему интересно.

возвращаясь к комментарию, на который я отвечал: видите ли, если «Детей спрашивают, что такое монитор, они отвечают контакт» — это означают, что их ничему не смогли научить. Значит, либо учили не тому, либо не так.
0
Colwin1 сентября 2010, 06:50#
Нет, почему же ) Например, бизнес-аналитику вовсе не обязательно понимать устройство байта. И отсутствие такого знания вовсе не мешает ему анализировать систему.
И еще — а действительно ли школьнику необходимо знать устройство телевизора? Физику-электронщику надо, программисту, пожалуй, тоже имеет смысл, а вот школьнику… не факт )
0
grimskin1 сентября 2010, 15:10#
ну почему же. в случае с ЭЛТ-шками телевизоры являлись хорошим примером применения ряда физических законов на практике.
+1
MAXH031 августа 2010, 20:20#
Меня один 9 класник доставал этим вопросом. Дам пусть грызет.
Респект автору. И забавное совпадение — с момента вопроса школьника прошло меньше недели.
+1
Fil31 августа 2010, 20:23#
Доставал именно структурой jpeg?
–1
MAXH01 сентября 2010, 12:52#
Да! BMP он освоил :) Кстати, хотел писать свой архиватор… Дам ему дискретку, пусть пишет.
Бредовый школьник — толи изобретатель очередной болгенос, то ли гений необструганный. Все начинает, все бросает на пол дороги. Писал игру на паскале — бросил, за 3 урока освоил Блендер отрубил мартышке ухо нарисованным мечем — забросил. Скачал игровой движок — строит свой CS. 

Это АД для учителя… Я за ним не успеваю. Программа ему не интересна.
Если получится вывезу его осенью на конференцию в Москву, может чуть мозга прибавит. А то быть самым умным на деревне — черевато манией величия.
0
mezastel1 сентября 2010, 18:03#
Плоховато вы к ученикам относитесь. Быть самым умным на деревне, при наличии хорошего стимула и поддержки, означает быть самым успешным в жизни.
0
MAXH03 сентября 2010, 10:55#
1. Я к нему плохо не отношусь. Иначе бы им не занимался, а гнал бы программу.
2. Я плохо отношусь к его привычке — снимать сливки и не лезь в глубь. Что-то нужно освоить, а не только по верхам бегать.
3. Его последняя фраза — «я за 3 дня освоил HTML». Я ее уже где то слышал. Поэтому поручил ему сделать поиск в Интернет по фразе Болгенос и сформулировать мнение.
Как то так.
+6
vk231 августа 2010, 20:41#
начните с более простых вещей, вроде BMP — там RLE обычный, если мне не изменяет память… затем GIF — тут уже LZW, пусть научится писать простой архиватор (это увлекательно же, я тоже в школе писал). А потом JPEG…
+1
shogunkub31 августа 2010, 21:12#
В BMP «по дефолту» нет сжатия вообще, легко проверяется по размеру — размер BMP точно равен (ширина*высота*BPP картинки)+заголовок. Поддержка RLE есть, но не знаю, насколько это актуально для простых графических редакторов. Возможно в более новых вариантах BMP есть что-то более продвинутое, чем RLE.
+1
vk231 августа 2010, 23:10#
да есть там RLE, и все всегда его умели, не спорьте :)

ну фиг с ним, с BMP

возьмите TIFF
или древний PCX (Paintbrush for DOS!)

по-моему, я с RLE/картинками как раз в PCX познакомился.

в общем, это было для примера — как демонстрировать прогресс в области алгоритмов компрессии (RLE — ведь действительно первое, что приходит в голову)
0
shogunkub31 августа 2010, 23:55#
>да есть там RLE, и все всегда его умели, не спорьте :)
Может и умели, достоверно всего не знаю. По стандарту он есть, так что спорить не буду, естессна :)
У TIFF тоже куча вариантов сжатия, да ещё и слои и прочая лабуда, слишком он навороченный. А вот PCX — это да, штука подходящая.
0
NickyX31 сентября 2010, 10:43#
У первых спецификаций TIFF было только LZW (во времена Aldus, от которой все досталось Adobe), а так же тег NewSubfileType давал делать только «страницы» внутри TIFF, хранение слоев приделала уже Adobe, им проще — они владельцы спецификации, могут расширять теги как угодно.

Такой же формат (за исключением хидера и в точном соответствии с первоначальными спецификациями и без сжатия) используется в формате Scitex CT
+1
shogunkub31 августа 2010, 21:14#
Википедия говорит, что есть как минимум JPEG и PNG(Deflate), по крайней мере у biCompression есть значения для этих алгоритмов.
+2
tyomitch31 августа 2010, 21:20#
Значения есть, но Windows такие файлы не поддерживает, я проверял.
Может быть, их какой-то редкий особый софт поддерживает.
+3
ehvadimka31 августа 2010, 20:53#
Вселенная не терпит нерешенных вопросов от девятиклассников
0
Colwin1 сентября 2010, 06:53#
Вселенная не терпит вопросов вообще ) Она просто дает ответы на те из них, на которые сочтет нужным ответить )
–3
Kuper31 августа 2010, 19:45#
Интересненько…
+3
biseptol31 августа 2010, 19:48#
Классная статья (я кстати, дочитал «до этого места», где мой пирожок). Правда непонятно зачем это все — для jpeg'а есть уже миллиард разных библиотек разной степени велосипедности. :-)
+12
Fil31 августа 2010, 19:50#
Как зачем? Интересно же самому поковыряться :)
0
Olostan1 сентября 2010, 01:41#
Не поймите неправильно, но все-таки интересно — неужели чисто и только академический интерес? Без практического применения и/или использования в качестве работы в университет или подобного.

Просто работа действительно не шуточная, особенно если в конце концов получился работающий декодер, и добиться не просто понимания как оно работает, а имплементации…

Кроме уважения к проделанной работе у меня все-таки просыпается легкое недоумение (и кажется не только у меня).
–2
beeruser1 сентября 2010, 04:02#
Потому что это базовые знания. 
Кто хочет развиваться — ищет то, чего не знает. А кто не хочет — сидит в болоте, расчитывая на чужие знания.
Реально раздражают люди, которые спрашивают — «а зачем ты это сделал», да «за тебя это уже сделали» и т.п.
Лучше сидеть вКонтакте и пересмотреть все тупые сериалы когда-либо созданные человечеством.

В средние века людей сжигали за тягу к знаниям, а теперь сжигают глупыми насмешками и глупыми вопросами.
0
Olostan1 сентября 2010, 10:13#
Простите, но я спрашивал не почему автор разобрался как оно работает, а есть ли этот труд где-то в применении (читай можно ли посмотреть готовую работу и где она применяется).
0
Fil1 сентября 2010, 10:19#
Конкретной цели не было, применения нет. Но раз уж сделал, была мысль сделать сделать свой просмотрщик, однако понимаю, что это довольно тяжело.
0
Olostan1 сентября 2010, 11:50#
Спасибо. 

А как Вы смотрите на реализацию многопоточного пакетного конвертора? Дело в том, что я заметил, что большество готовых конверторов совершенно не утилизируют 2+ ядерные системы (тот же FastStone когда я ему скормил 1к фотографий мрачно взял 80% одного ядра на моем квадре, и отрабатывал довольно долго). 

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

На стандартных декодерах такое сложно сделать, разве что паралельно загружать картинки, но разбить загрузку на этапы… не уверен.

В общем, вот такая идейка, куда реально можно было бы применить полученный результат.
0
Fil1 сентября 2010, 12:00#
Параллелится отлично, но действительно, это редко используется. Кодирование посложнее. Подумаю, спасибо за совет :)
0
beeruser1 сентября 2010, 23:15#
Параллельно можно обрабатывать и 1 картинку.
В случае декодирования, по сути, всё упирается в скорость последовательного участка — разборки huffman-а. DCT/color-transform можно разложить на любое число тредов.

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

У меня в PS3 SPU реализации Jpeg декодера около 70% уходило на huffman, но правда я не оптимизировал этот участок, а оптимизировать там есть что. В принципе декодер и так давал 40-50MPix/s с 1 SPU — так что я не особо загонялся по этому поводу.

>> Или паралельный поиск оптимального алгоритма сжатия с анализом потерь
У jpeg один алгоритм (не считая lossless)
Но можно играть таблицей квантизации и фильтровать блоки 8x8 чтобы они занимали меньше памяти после DCT.
0
Fil2 сентября 2010, 15:32#
Гм, я тоже распараллеливал. Но только ДКП, так как это жрало процентов 80. На двухядерном E8500 стало выдавать примерно такую же скорость. А разбор битового потока шел довольно быстро.
0
beeruser1 сентября 2010, 03:51#
лучше что-то знать и уметь чем не знать и не уметь

–1
Colwin1 сентября 2010, 06:54#
Пример — работа с очень большими изображениями ) почти все готовые либы работают с JPEG в памяти. А если памяти не хватает? Вот тут уже приходится и ручками покопаться, и алгоритмы понапридумывать )
+4
DeadFine31 августа 2010, 19:49#
Большое спасибо, очень качественная статья!
+35
inspush31 августа 2010, 19:51#
Блин, торт! Тортище!
+1
vmysla31 августа 2010, 19:52#
А если не секрет, зачем вы писали свой декодер? Довольно специфичная задача
+1
PDEMON31 августа 2010, 19:53#
Спасибо. Не знал что в фавиконке гугла столько «Яд»а :)
0
Fil31 августа 2010, 19:59#
Он есть во всех jpg-файлах :)
+2
Pollux31 августа 2010, 19:59#
Вы маг! :)
–5
root_sashok31 августа 2010, 20:01#
Месье знает толк в извращениях!
+1
Palehin31 августа 2010, 20:09#
Помнится читал что в GIF изображения можно без труда поместить php код и большинство загрузчиков файлов пропустят такое изображения и код корректно выполнится на сервере. А что с jpg в этом случае?
+1
Fil31 августа 2010, 20:20#
Можно поместить что угодно(если какие-либо 2 байта не будут совпадать с маркером) после окончания любой из секций, у которой известна длина. Декодировщики, после чтения секции пропускают байты пока не встретят маркер. Как это применимо в плане помещения туда php-кода я не знаю.
–1
vk231 августа 2010, 20:44#
Вы читали какую-то желтую прессу, простите )

Максимум, что могло быть — это какая-нибудь ошибка типа stack overflow в декодере чего-то вроде ImageMagick/GD. Вы заливаете туда файл, php-скрипт его пытается сконвертировать, из картинки вылезает код и с правами GD начинает творить чудеса. Что-то вроде этого
0
Palehin31 августа 2010, 20:59#
я читал вот это -> «Марсель Низамутдинов — Тактика защиты и нападения на Web-приложения»
Других книг на русском языке по безопасности я увы не нашёл…
+1
s1im1 сентября 2010, 08:48#
скорее всего, там имелось в виду следующее:
* старые непропатченные версии ИЕ6/ИЕ7 могут выполнить js код спрятанный в картинке;
* если разработчик не сделал обработку картинки после ее загрузки на сервак и она тупо копируется в папку доступную из веб, то можно загрузить картинку в теле которой будет php-скрипт и попытаться выполнить его (например, при наличии уязвимости класса php-including);

www.dsec.ru/about/articles/web_xss/
0
Lux_In_Tenebris1 сентября 2010, 00:49#
никто не мешает дописать что угодно в конец файла :)
0
Lux_In_Tenebris1 сентября 2010, 00:52#
Сам по себе PHP из картинки, естественно, не выполнится просто так, как ни старайтесь. Для этого требуется убедить интерпретатор (часто путём добавления .php в середину или конец имени файла) выполнить файл, содержащий картинку и добавку в виде PHP кода.
+1
ZiNTeR31 августа 2010, 20:19#
Отсюда вывод: правоверный jpeg начинается с «яШяю» :)
0
Fil31 августа 2010, 20:22#
Не совсем :) Начинается всегда «яШ». Но порядок остальных маркеров(кроме SOS, конца файла, и еще некоторых) не регламентирован.
+1
likenoother31 августа 2010, 20:24#
[00 11] Длина: 16 байт. 
17
+1
Fil31 августа 2010, 20:27#
О, недоглядел, спасибо за внимательность!
+2
iosis31 августа 2010, 20:25#
оу, классно! Разжевано хорошо достаточно! спасибо.
0
BReal31 августа 2010, 20:25#
Не совсем понял, это jpeg картинка, сделанная из ico?
+2
Fil31 августа 2010, 20:29#
Верно. От ico там ничего нет.
+2
Fil31 августа 2010, 20:31#
А пережал так сильно, чтобы уменьшить размер, и в особенности деревья Хаффмана.
0
Banzeg31 августа 2010, 20:28#
Хорошо расписано, с картинками :)
В вузе на младших курсах развлекался, ковыряя все подряд в hex-редакторах, изучая форматы. После таких упражнений спецификации читались как худлит.
–4
BarsMonster31 августа 2010, 20:30#
Простите, а зачем надо с нуля писать? Реализаций навалом )
+3
Fil31 августа 2010, 20:32#
Я уже отвечал
0
BarsMonster31 августа 2010, 20:34#
Ясно… Круто, сам ковырял просто тоже :-)
+3
Magnifico31 августа 2010, 20:36#
«Вам когда-нибудь хотелось узнать как устроен jpg-файл? Сейчас разберемся!» — говорится в сааамом начале текста ;)
0
shogunkub31 августа 2010, 20:31#
Офигенно, спасибо. Пока не успел прочитать, сижу работаю сверхурочно, но именно такую статью по структуре графических файлов в своё время искал. Может напишете серию про различные распространенные мультимедиа-форматы? Интересно, и адски полезно для многих.
+6
Fil31 августа 2010, 20:35#
Пожалуйста! Я подумаю. Это занимает много времени, но может на досуге еще что-нибудь расковыряю :)
+1
shogunkub31 августа 2010, 20:38#
Радует, что любознательность еще не все растеряли. Благодаря таким как вы, хабр ещё торт ;)
0
Colwin1 сентября 2010, 06:59#
Если надумаете, может, на сайте своем выложите?
И людям искать удобно, и свой ресурс продвинете уникальным и ценным контентом.
+1
Zubchick31 августа 2010, 20:34#
Хотелось бы что бы после этой статьи число студентов, собственноручно написавших курсовой по структурам и алгоритмам, увеличилось хотя бы на толику :)
0
Colwin1 сентября 2010, 07:00#
Главное, чтобы не старые алгоритмы заново реализовывали, а новые идеи находили ) Иначе большого смысла нет.
0
webrover31 августа 2010, 20:36#
Вы как оптимизировали ДКП? насколько я помню, каконичный алгоритм оперирует произведением матриц. SSE накручивали?
0
Fil31 августа 2010, 20:46#
Я думал насчет SSE. Но мне показалось компилятор сам, при определенных опциях, использует SSE-оптимизацию. Но не могу это утверждать.
0
Fil31 августа 2010, 20:54#
Моя оптимизация простая — формулу свел к перемножению матриц, косинусы заменил массивом констант, и выделил некоторые частные случаи, когда в матрице преобладают нули.
0
webrover31 августа 2010, 21:14#
ну тоже самое что и RES*IMG*DCTT

> Но мне показалось компилятор сам, при определенных опциях, использует SSE-оптимизацию. 
некоторые преобразуют раскрытые циклы…
0
Fil31 августа 2010, 23:05#
Я тоже читал статью на codenet :) Что-то они там намудрили. IMG — исходная матрица, RES — полученная матрица ДКП, но что тогда значит RES*IMG? Вероятно они имели в виду RES = DCT*RES*DCTT
+8
cooch31 августа 2010, 20:45#
Если интересно — могу рассказать о своих опытах расковыривания формата Fruity Loops Score (fsc).
0
gionet31 августа 2010, 23:58#
Вот это было бы тоже интересно! Давайте
0
andrew_b31 августа 2010, 20:50#
Интересно, а на сколько реально «рисовать» jpeg в hex редакторе?
+4
shogunkub31 августа 2010, 21:06#
В HEX-редакторе реально сделать всё что угодно. Вопрос только в навыке и наличии большого количества свободного времени :)
0
Colwin1 сентября 2010, 07:02#
Насчет большого количества — это вы в самую точку )
Зато компенсируется самоудовлетворением )
НЛО прилетело и опубликовало эту надпись здесь
–2
main31 августа 2010, 21:27#
Перечитал все комментарии, удивляюсь, как это никто не всунулся и не написал:
Хабр ТОРТ!

Статья зачОт!
0
Garrett31 августа 2010, 22:22#
Выше таки написали что-то вроде =)
0
main2 сентября 2010, 09:53#
Чорт, мимо :)
+1
thoth31 августа 2010, 21:41#
Статья хорошая, понравилась. 
Ностальгия. Как то писал на Verilog, JPEG-encoder. Ну и соответственно пришлось постигать так же процесс построения самого JPEG файла.
+1
assiduus31 августа 2010, 21:49#
Ооо… мой 4 курс, предмет — цифровое телевидение :)
+1
KwI31 августа 2010, 22:05#
Класс! Спасибо огромное!
Очень приятно, что кратко и понятно.
0
olegi31 августа 2010, 22:17#
До сих пор помню, она занимала 7 секунд. Но это и неудивительно, если бездумно пользоваться приведенной формулой, то для вычисления одного канала одного пикселя потребуется нахождения 128 косинусов, 768 умножений, и сколько-то там сложений. Только вдумайтесь — почти тысяча непростых операций только на один канал одного пиксела! К счастью, тут есть простор для отимизации (после долгих экспериментов уменьшил время загрузки до предела точности таймера 15мс, и после этого сменил изображение на фотографию в 25 раз большей площадью. Возможно, напишу об этом отдельной статьей).

я только за
0
Bas1l31 августа 2010, 22:38#
На самом деле, в качестве отправной точки оптимизации надо брать быстрое преобразование Фурье:en.wikipedia.org/wiki/Fast_Fourier_transformen.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
0
Fil31 августа 2010, 22:54#
Да, я на них тоже наткнулся, но так и не решился разбираться.
0
thoth1 сентября 2010, 14:21#
Согласен, во первых FFT. А во вторых косинусы не пересчитывать а держать константами. (ха, капитан очевидность)
0
ripsomeday31 августа 2010, 22:21#
большое спасибо, как раз сам сидел разбирался.
вот бы еще про lossless jpeg так подробно расписали. эх.
0
avz31 августа 2010, 23:38#
Хм. А мне казалось что в jpeg'е обязательно должен быть JFIF
0
Fil31 августа 2010, 23:52#
А, да, JPEGsnoop говорит, что это идентификатор. Он находится в секции с маркером FFE0. А все секции с маркерами FFEn отданы для приложений. В них же хранится и EXIF. Возможно есть договоренность по этому идентификатору JFIF, но спецификация этого не предусматривает. Поэтому я просто удалил эту секцию.
+1
demoded31 августа 2010, 23:53#
вагон респекта за столько труда!
+1
Vordigont1 сентября 2010, 01:09#
Супер! То что я искал! Спасибо автору!!!
0
Christmas1 сентября 2010, 01:26#
Я так когда-то при помощи бинарного просмотрщика (в Тотал Коммандере) и пэйнта без посторонней документации разобрал полноцветный bmp, написал генератор на php и рисовал прямые линии :-)
Пользы ноль, удовольствия — тонна.
0
vk21 сентября 2010, 01:52#
насколько «прямые линии»? алгоритм Брезенхейма? )
0
Christmas1 сентября 2010, 02:51#
Да вы что, исключительно вертикальные и горизонтальные :-) Я в том юном возрасте даже фамилию такую прочитать не смог бы)
0
Christmas1 сентября 2010, 03:01#
Хотя пробовал рисовать под углом — понял, что ничего не смогу, а про алгоритмы читать негде — интернета не было.
+1
Colwin1 сентября 2010, 07:05#
Полноцветный — вполне возможно )
А вот если бы 16-битный формат разобрал — я бы медаль дал ) таких гениев мало )
0
b8c61 сентября 2010, 11:43#
А какие с ним сложности? Что два байта на три цвета?
Сам не разбирал, но не думаю, что это задача уровня гения.

А JPG не так подробно, но ковырял: надо было быстро получать из файла размеры картинки и EXIF (чтобы скриптом перелопатить под миллион картинок): так прочесть пару байт из файла оказалось куда быстрей, чем использовать готовые библиотеки, которые грузят картинку в память (может, есть такие, которые не грузят, но за полдня я таких не нашёл).
–2
Alvein1 сентября 2010, 10:52#
Я бы засунул эту статью в перевод, т.к. текст очень похож на англоязычный вариант того, по которому я учился декодить JPEG примерно 8 лет назад. Писал программу на Си для конвертации картинок в ANSII графику…
+1
Fil1 сентября 2010, 11:31#
Какой еще перевод! Я потратил все вечера недели на написание статьи. Сам взял пример, сам декодировал его, сам нарисовал схемы и деревья, сам получил эти таблицы, сам описал процесс. А процесс декодирования он один для всех, поэтому похож.
+1
Alvein1 сентября 2010, 11:34#
возможно в тот момент спецификация была не 180 страничная. Пороюсь в документах и попробую привести то что было. А за работу спасибо, прав я или нет, перевод это или нет, главное что работа проделана и проделана качественно и, думаю, поможет многим интересующимся и перспективным пиплам, коих с каждым годом не становиться больше :(
0
bbbb1 сентября 2010, 11:28#
Спасибо, напомнили мне о студенчестве.
Неделю этот алгоритм в Борланд Паскале отлаживал :))
0
VYBGSS1 сентября 2010, 12:22#
О души благодарю автора статьи. Впрочем — количество добавивших избранное показывает, что я не один :).
0
Fil1 сентября 2010, 12:23#
Рад, что понравилось! Я сам в шоке от количества :)
+2
kurokikaze1 сентября 2010, 13:03#
Супер!!! Будет что нибудь подобное для PNG?
0
Fil1 сентября 2010, 13:19#
Спасибо :) Возможно, но на это уходит куча времени.
0
Alkzndr1 сентября 2010, 13:46#
Я бы добавил ссылку на Independent JPEG Group откуда можно скачать исходники на С. Там же можно посмотреть на используемые оптимизации, так, например, есть несколько реализаций ДКП и ОДКП. Следует правда отметить, что код написан очень мудрёно. 

Также когда-то попадалась на глаза реализация от Intel.
+1
anarsoul24 февраля 2011, 01:46#
У вас картинка для дерева DC, id =0 неправильная, в узлах должны быть значения 0x03 и 0x02
+1
anarsoul24 февраля 2011, 01:52#
И для маркера DHT класс таблицы у вас неверно описан, должно быть:
класс: 0 (0 — таблица DC коэффициэнтов, 1 — таблица AC коэффициэнтов).
0
Fil24 февраля 2011, 08:19#
Блин, я невнимательный. Спасибо за бдительность, поправил!
0
DjOnline16 октября 2011, 12:42#
Из-за грёбанных патентов до сих пор в jpeg не применяется арифметическое сжатие.
А есть ведь ещё разработки типа типа PackJPG (и другие), обеспечивающие на 30% лучшее сжатие чем обычный jpeg при абсолютном байт-в-байт сходстве распакованной картинки, но тоже проблема — никаких плагинов для браузеров/просмотрщиков/редакторов.
0
DjOnline16 октября 2011, 20:54#
p.s. хотя патенты то уже несколько лет как кончились.