Я наколхозил для собственных нужд на основе проекта minidjvu консольную утилиту djvudict (назвал от балды, можно подобрать по-лучше).
Это - сырая-пресырая альфа, которую я возможно до ума доводить и не буду, т.к. я от нее и так могу добиться чего лично мне надо, а для стороннего пользователя такая штука не факт что и нужна.
Утилита предназначена для исследования словарей, кодированных в DjVu документе.
В отличие от djvudump из пакета DjVuLibre, она не просто выдирает чанки Djbz и Sjbz, но разбирает их на части, сохраняя элементы в формате BMP, а также считая статистику их использования на странице и в документе в целом.
Исходники и подробное описание на английском тут:
https://github.com/trufanov-nok/djvudictБинарники, в т.ч. под Win64, тут:
https://github.com/trufanov-nok/djvudict/releasesЯ последнее время задался вопросом недостаточно, на мой взгляд, большого сжатия, достигаемого свободными opensource кодировщиками при кодировании многостраничных DjVu документов, по сравнению с проприетарными. Я кодировал одни и те же ч/б документы разными кодировщиками и использовал djvudict чтобы оценить организацию в них словарей и статистику их использования на страницах.
Простыми словами о теории, как я ее понимаю:Если вкратце, то кодировщики сегментируют оригинальное изображение на то, что будет восприниматься как ч/б текст и то, что будет восприниматься, как изображение. Эти сегменты (чанки, chunk) кодируются разными методами и упаковываются в единый документ, что и можно назвать foreground и background слоями. Собственно, особую компактность по сравнению с другими форматами, DjVu документы достигают за счет компрессии именно слоя текста.
Текст кодируется в изображении в формате JB2, которые хранятся в документе в сегментах с id Sjbz, по одному на страницу. JB2 по-сути, является набором инструкций. Каждая инструкция может кодировать небольшой символ (букву), просить отрисовать его на изображении в заданной позиции И/ИЛИ может просить "запомнить его" для последующего использования. Следующая инструкция может не кодировать символ, а просто просить отрисовать на странице запомненный символ в новом месте.
Т.о. при последовательном чтении инструкций из JB2 изображения создается некий локальный словарь, и за счет его использования достигается компрессия. Это важно - даже в одностраничном документе всегда есть словарь.
Логично, что если у вас многостраничный документ, то словари символов на соседних страницах могут иметь много общих символов, и можно достичь куда большей компрессии сделав для них общий словарь. Этот общий словарь создается обычно на каждые 10-20 страниц и сохраняется в сегменте Djbz в формате такого же JB2 изображения (авторы не стали выдумывать велосипед). Только такое JB2 изображение имеет размеры 0x0 и в нем нет инструкций, которые бы просили что либо отрисовать - только запомнить для будущего использования.
Т.о. общие словари Djbz - это такие же изображения, как и Sjbz, только они 0-го размера и на них ничего не отрисовывается. Если JB2 изображение страницы из Sjbz сегмента кодировано с использованием общего словаря, то самой первой инструкцией будет требование найти этот общий словарь по id и считать из него N символов в свой локальный словарь. После чего JB2 изображение строится как обычно. Т.о. общий словарь просто инициализирует локальный словарь страницы.
Стоит добавить, что сами симолы в JB2 формате могут кодироваться как прямо (direct coding), так и на основе некого прототипа (refinement coding - уточняющее кодирование?). Во втором случае кодируется не каждый пиксель символа, а указывается символ-прототип (по индексу в словаре) и разница между ним и тем, что кодируется. Это позволяет дополнительно сэкономить на хранении похожих букв, например Ш и Щ. При этом прототип сам может быть кодирован на основе прототипа. Такое кодирование используется в том числе в рамках общего словаря (Djbz), где ничего не отрисовывается.
Что на самом деле делает djvudictУтилита djvudict ищет в DjVu документе (поддерживается bundled multi-page) все JB2 изображения (Djbz и Sjbz сегменты) и для каждого вываливает в его подпапочку те символы локального словаря, которые в нем напрямую закодированы. Т.о. если этим JB2 изображением является общий словарь Djbz - то все его символы будут сохранены в bmp. Если это Sjbz в single-page DjVu (кодируется без Djbz) - то будут сохранены все символы из его локального словаря. Если это Sjbz в multi-page DjVu - то будут сохранены те символы локального словаря, которые не заимствованы из общего Djbz словаря.
Кроме того, сохраняется лог последовательности типов инструкций в JB2 изображении с индексами. Ведется статистика использования этих инструкций по странице и в документе. Сообщается размеры словарей, кол-во символов на странице, число обращений к локальному или общему словарю, число прямо и уточняющее кодированных символов. Также для Sjbz в папке рендерится BMP всей страницы. В идеале хотелось бы буквы цветом на ней маркировать, но лучшее - враг хорошего.
Ну и смотря на это можно пытаться угадать, за счет чего один ч.б. документ сжатый проприетарным кодировщиком на пол мегабайта меньше такого-же, сжатого свободным кодировщиком. Где там слишком маленький или большой словарь, слишком плохое его использование, где плохо ищутся прототипы для уточняющего кодирования и пр.
Я в отдельном посте напишу свои мысли по тому, что можно было бы в этом направлении сделать в кодировщике minidjvu.