Страницы

Поиск по вопросам

Показаны сообщения с ярлыком сжатие. Показать все сообщения
Показаны сообщения с ярлыком сжатие. Показать все сообщения

воскресенье, 15 марта 2020 г.

Существуют ли сторонние сервисы, которые могут разархивировать данные, поступающие из API StackOverflow?

#json #сжатие #stackoverflow_api


Вместо JSON получаю по API набор цифр. Сказали это сжатые данные.

Вот сам запрос:

https://api.stackexchange.com/2.2/search?order=desc&sort=activity&intitle=corezoid&site=ru.stackoverflow.com


По клику все работает нормально. Если использую postman, тоже. Мне сказали, что postman
автоматически разархивирует сжатые данные.

Данные, которые поступают мне в систему по API выглядят так:

{
    "create_time": "2015.11.25 18:09:04",
    "change_time": "2015.11.25 18:09:04",
    "node_prev_id": "5655caeaf6c37670c306ddef",
    "status": "processed",
    "user_id": 5781,
    "data": {
            "__conveyor_api_return_description__": "Not valid http result json(<<31,139,8,0,0,0,0,0,4,0,171,86,202,44,73,205,45,86,178,138,142,213,81,202,72,44,142,207,205,47,74,85,178,74,75,204,41,78,213,81,42,44,205,47,73,140,207,
77,172,80,178,50,54,48,128,241,139,82,115,19,51,243,50,243,210,149,172,140,44,141,106,1,178,195,112,174,67,0,0,0>>)",
            "__conveyor_api_return_http_code__": 200,
            "__conveyor_api_return_type_tag__": "api_no_valid_json",
            "__conveyor_api_return_type_error__": "software"
    }
}

    


Ответы

Ответ 1



Какой библиотекой/программой вы делаете запрос? Выкиньте ее и забудьте, раз в 2015м году она не умеет декодировать gzip. Вообще говоря, это ошибка на стороне stackoverflow api. По стандарту HTTP они не должны сжимать ответ без заголовка запроса Accept-Encoding: gzip - но сжимают. Можете попытаться сообщить им о баге. Или можете запросить новую фичу у разработчиков вашей библиотеки. Если же вам надо декодировать сообщение, не меняя клиентскую библиотеку и не дожидаясь исправлений от разработчиков - то такой "сторонний сервис" есть на любом линуксе: команда gzip -d сделает то, что вам надо. Под виндой можно поставить cygwin. Также для многих языков программирования есть библиотеки для декодирования gzip. К сожалению, вы не указали на чем пишете, поэтому не могу посоветовать вам конкретную.

Ответ 2



Сервер посылает сжатые данные, только если сжатие поддерживается на стороне клиента. Например клиентом при запросе GET/POST в Request передается: Accept-Encoding: gzip, deflate, lzma, sdch тогда сервер может перед отправкой сжать данные и отправить. При этом в Response он сообщит какой метод сжатия использовался: Content-Encoding: gzip API StackOverflow, почему то игнорирует запрос пользователя и сжимает с помощью gzip все данные, так что проблема на стороне сервера, вам лишь остается каждый раз разархивировать данные.

пятница, 14 февраля 2020 г.

Как связаны zlib, gzip и zip? Что у них общего и какие у них отличия?

#zip #png #gzip #сжатие #zlib


Алгоритм сжатия, используемый в zlib, почти тот же, что и в gzip и zip. Чем они отличаются?
    


Ответы

Ответ 1



tl;dr: zip — формат архивов, использующий, как правило, алгоритм Deflate для сжатия; zip-архив может содержать несколько файлов, которые сжимаются отдельно друг от друга. gzip сжимает ровно один файл (этим одним файлом может быть tar-архив) и тоже использует алгоритм Deflate. Библиотека zlib реализует Deflate и используется в zip, gzip, png и многих других приложениях. Формат zip был разработан Филом Кацем как открытый формат, но его реализация, PKZIP, была условно-бесплатной (shareware). В этом формате архива хранятся файлы и структура каталогов, и каждый файл сжимается независимо от других файлов. Файлы и структура каталогов также могут быть зашифрованы. Формат ZIP поддерживает несколько методов сжатия: 0 - The file is stored (no compression) 1 - The file is Shrunk 2 - The file is Reduced with compression factor 1 3 - The file is Reduced with compression factor 2 4 - The file is Reduced with compression factor 3 5 - The file is Reduced with compression factor 4 6 - The file is Imploded 7 - Reserved for Tokenizing compression algorithm 8 - The file is Deflated 9 - Enhanced Deflating using Deflate64(tm) 10 - PKWARE Data Compression Library Imploding (old IBM TERSE) 11 - Reserved by PKWARE 12 - File is compressed using BZIP2 algorithm 13 - Reserved by PKWARE 14 - LZMA (EFS) 15 - Reserved by PKWARE 16 - Reserved by PKWARE 17 - Reserved by PKWARE 18 - File is compressed using IBM TERSE (new) 19 - IBM LZ77 z Architecture (PFS) 97 - WavPack compressed data 98 - PPMd version I, Rev 1 Методы с 1 по 7 являются историческими и не используются. Методы с 9 по 98 добавлены относительно недавно и используются нечасто. Единственным широко распространённым методом является метод 8, Deflate и, в некоторой степени, метод 0, который просто хранит файлы без сжатия. Большинство zip-файлов, с которыми вы столкнётесь, будут использовать только методы 8 и 0. Стандарт ISO/IEC 21320-1:2015 для файловых контейнеров является ограниченным форматом zip, используемым в файлах Java (.jar), Office Open XML (Microsoft Office .docx, .xlsx, .pptx), Office Document Format (.odt, .ods, .odp) и EPUB (.epub). Этот стандарт допускает только методы сжатия 0 и 8, а также имеет другие ограничения вроде отсутствия шифрования или подписей. Примерно в 1990 году группа Info-ZIP написала переносимые и свободные реализации утилит zip и unzip с поддержкой сжатия Deflate и распаковки более старых форматов. Это значительно расширило использование формата .zip. В начале 90-х формат gzip был разработан для замены Unix-утилиты compress. Утилита compress сжимала ровно один файл и добавляла к файлу расширение .Z. Она использовала алгоритм LZW, который в то время защищался патентами, что затрудняло его использование. Хотя некоторые конкретные реализации Deflate были запатентованы Филом Кацем, сам формат патентами не защищается, так что можно написать реализацию Deflate без нарушения патентов. Утилита gzip была задумана как прозрачная замена утилиты compress и фактически может распаковывать сжатые compressом данные. gzip при сжатии добавляет расширение .gz к имени файла. gzip использует формат сжатия Deflate, который позволяет сжимать немного лучше чем compress, быстро распаковывается и проверяет целостность данных с помощью CRC-32. Заголовок формата gzip также позволяет хранить исходное имя файла и время его изменения. Утилита compress сжимает ровно один файл, и для сжатия нескольких файлов с сохранением атрибутов и структуры каталогов обычно создавались архивы tar, которые затем сжимались с помощью compress для получения .tar.Z файла. Чтобы не сжимать всё вручную, утилита tar имела и до сих пор имеет опцию для автоматического включения сжатия с помощью compress; с появлением gzip также добавилась опция для включения gzip-сжатия и создания .tar.gz файлов. Архивы .tar.gz сжимаются лучше чем zip, так как файлы сжимаются не по отдельности и gzip имеет возможность эффективнее работать с избыточностью, особенно если в архиве много мелких файлов. .tar.gz широко распространён в Unix из-за его хорошей переносимости, но есть и более эффективные методы сжатия, так что могут встретиться архивы .tar.bz2 и .tar.xz. В отличие от .tar, .zip архивы хранят список файлов отдельно в конце файла. С учётом того, что каждый файл сжимается независимо, это позволяет прочитать отдельные файлы в архиве без распаковки всего архива целиком. А архив .tar должен быть распакован и прочитан целиком, чтобы даже просто увидеть список файлов в архиве, и это создаёт трудности в некоторых ситуациях. Вскоре после появления gzip, примерно в середине 1990-х годов, те же проблемы с патентами поставили под вопрос бесплатное использование формата изображений .gif, очень широко используемого на досках объявлений (BBS) и во всемирной паутине (WWW — новинка того времени). Так что небольшая группа создала PNG — формат изображений со сжатием без потерь — чтобы заменить GIF. PNG тоже использует формат Deflate для сжатия. Чтобы способствовать широкому использованию формата PNG, были созданы две свободные библиотеки: libpng реализует все функции формата PNG, а zlib предоставляет код сжатия и распаковки для использования в libpng и для других приложений. zlib был адаптирован из кода gzip. Все упомянутые патенты уже истекли. Библиотека zlib поддерживает Deflate, а также три обёртки (wrapping) для потоков Deflate. К ним относятся: отсутствие обёртки вообще («сырой» Deflate), обёртка zlib, который используется в блоках данных формата PNG, и обёртка gzip. Основное различие между обёртками zlib и gzip в том, что zlib-обёртка более компактна: всего 6 байт против как минимум 18 байт для gzip, а проверка целостности Adler-32 выполняется быстрее, чем CRC-32, который используется в gzip. Сырой метод Deflate используется программами, которые читают и записывают формат .zip, который по-своему работает с данными Deflate. Библиотека zlib используется очень широко в том числе в качестве реализации формата gzip; например, она используется в большинстве браузеров и веб-серверов для сжатия передаваемых в HTTP данных (при этом обычно используется формат gzip, так как с форматом zlib возникли некоторые исторические трудности). Разные реализации Deflate могут сжимать одни и те же данные с разной эффективностью, о чем свидетельствует наличие выбираемых уровней сжатия, которые позволяют выбирать между эффективностью сжатия и затраченным временем. zlib и PKZIP - не единственные реализации Deflate. 7-Zip или zopfli могут затратить очень много времени, чтобы сжать данные на несколько процентов эффективнее чем zlib. Утилита pigz, многопоточная реализация gzip, может использовать zlib (уровни сжатия 1-9) или zopfli (уровень сжатия 11) и несколько уменьшает затрачиваемое время с помощью сжатия больших файлов на нескольких ядрах процессора. Слегка вольный перевод ответа от Mark Adler на enSO с некоторыми дополнениями.

суббота, 21 декабря 2019 г.

Как скомпрессировать набор натуральных чисел? Порядок неважен, повторов нет.

#множества #сжатие #алгоритм #сравнение #компрессия


Задача сравнить два набора уникальных целых положительных чисел, и найти присутствующие
сразу в обоих. Все точно лежат в диапазоне от 1 до 200 млн. Обычно в каждом из двух
наборов от 0 до 5 млн чисел.
До сих пор делаю "в лоб": оба сета заношу во временные таблицы MySQL. Две одноколоночные
таблицы, где числа – первичные ключи. Сравнение проходит быстро, если сеты маленькие
и помещаются в engine=MEMORY. Медленно, когда таблицы большие и приходится создавать
их на диске. Когда надо таких сравнений выполнять помногу и часто — тормоза.
Что, если воспроизвести индексированные колонки MySQL в собственном коде? Один из
сетов держать в памяти, а каждый элемент второго проверять на наличие в первом.
Не хранить каждое из чисел набора (32бит, 2.5млн в среднем = 80Мб), а работать с
битовой маской всех возможных значений. 200 млн это, с запасом, 2^28 = 268,435,456
бит = 32Мб. Установлен – число есть в наборе, 0 – нет. Сравнивать установленные биты. 
В полном виде хранить для каждого сета весь набор битов неэффективно. Наверняка,
можно такие данные здорово компрессировать. Большинство битов будут 0, значит, их последовательности
можно кодировать их кол-вом подряд например. 
Вопрос к такому компрессированному массиву будет один: есть ли очередное искомое
число в наборе, или нет?
Упростим для примера. Пусть всего может быть 32 значения: 0..31. Наш массив будет
состоять из 32 нулей/единиц. В наборе присутствуют всего два значения: 17 и 22. 16
нулей, единица, 4 нуля, 1. И запишем их как 16,4: 10000100. Всего 8 бит вместо 2*6.
компрессия сэкономила 25%. Но это моё совсем косолапое представление о возможном способе
компрессии, без разделителей, единиц подряд и т.п.
Надо узнать про число 19, есть ли в наборе? Проходим по нашим 8 битам: 16 ещё пока
меньше 19, ещё 4 — уже перебор, ответ "нет в наборе".
Как по-вашему, есть ли вообще смысл в таком велосипеде, может ли он ускорить сравнение
двух сетов, по сравнению с MySQL?
Upd. Проще сформулирую вопрос. Ищется компрессия для данных, когда известны их параметры
и ограничения: только натуральные числа от .. до .., не подряд, не сортированные, без
повторов, порядок неважен. И даже без необходимости распаковки: нужно лишь уметь ответить
на вопрос «есть ли такое-то число в наборе, или нет?».    


Ответы

Ответ 1



1) БД не для решения таких задач 2) используем либо упорядоченные списки, либо хештаблицы 3) я бы использовал упорядоченный список или массив, и потом сравнивал методом попарного слияния: - оба указателя на первые элементы берем элемент с первого списка, сравниваем с элементом второго списка если значения равны, то элемен заносим в список результата и увеличиваем оба указателя и к пп 1 если 1 < 2, увеличиваем значение указателя первого списка иначе второго итого, используемые алгоритмы: 2 * qsort и попарного слияния если списки ну очень большие...(20 млн вполне терпимо для памяти, но 200 может быть уже перебором), то их можно разбить на части и сравнивать сперва часть 1, потом часть 2. Алгоритм приблизительно следующий: есть списки 1: А+Б+С+Д... и 2:А+Б+С+Д... - сравниваем список 1А и 2А если список 1А закончился, то сравниваем конец списка 2А и список 1Б иначе конец списка 2Б со списоком 1А как только какой список заканчивается текущим становится следующий из данной последовательности (1 или 2 ) и так далее

Ответ 2



Я бы начал с простых решений и замеров времени выполнения для разных наборов данных. Вы можете всегда хранить данные в заранее подготовленном виде (Например уже в бинарном дереве или в отсортированном массиве)? Тогда находить элементы можно примерно так: // для отсортированным массивов. Сложность О(a.length + b.length) public static List GetSameNumber(Int32[] a, Int32[] b) { var i = 0; var j = 0; var ans = new List(); while (i < a.Length && j < b.Length) { if (a[i] == b[j]) { ans.Add(a[i]); i++; j++; } else if (a[i] > b[j]) j++; else i++; } return ans; } // для бинарных деревьев. Сложность О(a.length) или О(a.length + b.length)в зависимости от реализации public static List GetSameNumber(SortedSet a, SortedSet b) { var ans = new List(); foreach (var i in a) { if (b.Contains(i)) ans.Add(i); } return ans; //var ansset = new SortedSet(a); //ansset.IntersectWith(b); //return ansset.ToList(); // или просто return ansset } Если данные нельзя хранить в нужном виде, то переводить в него каждый раз при вызове. Тут появляется О(nlogn) для создания бинарного дерева или сортировки массива. Большое дерево с битами на 8Гб наверно даст пенальти по кешу и пейджингу.

пятница, 13 декабря 2019 г.

Можно ли предсказать максимальный размер png изображения?

#png #сжатие #clipboard


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


Ответы

Ответ 1



Вы задали сложный и интересный вопрос. Точного ответа на который скорее всего ни у кого нет. Хотя Вы можете поискать какие-нибудь оценки алгоритма сжатия DEFLATE, который используется в PNG. К слову, о PNG. Это формат хранения картинок со сжатием данных без потерь, в котором используется алгоритм DEFLATE. Метод не из самых простых. Его достаточно хорошее описание мной найдено было найдено здесь, здесь, частично тут и вот тут. Но в силу того, что мои интересы лежат немного в стороне от сжатия данных, вспоминать коды Хаффмана в различных вариациях я не стал, а избрал иной, более дешёвый подход к решению задачи. Он даёт худший результат и не точный результат. Но, при должном развитии идеи, которую я опишу, можно получить достаточно качественные нижнюю и верхнюю оценки. Нужно также понимать, что используюя данный подход, всегда можно будет подобрать такое изображению, которое будет выбиваться из общей динамики картинок. В силу того, что я не знаю, что и как Вы хотите оптимизировать, приведу лишь основные положения того, что можно делать. Всё ниже перечисленное снабжу кодом на python 2.7. Готового решения, к сожалению, я не дам, посколько не знаю целей. Но на основе моих наработок можно получить очень хороший результат. Насчёт точной оценки. Её скорее всего можно получить, изучив то, как ведут себя коды Хаффмана. Но это будет трудоёмкий процесс. Вопрос в том, насколько это Вам нужно. Общая идея подхода состоит в том, чтобы выявить зависимость , где -- параметры, которые будем задавать. Указанную зависимость мы будем получать, рассматривая некоторое множество изображений (у меня на компьютере есть база со стоковыми фотографиями профессиональных фотографов). Эти изображения мы будем рассматривать как объекты. Выше указанную зависимость мы будем строить, основываясь на выборке картинок. Если у Вас нет python и IDE для него, то я советую Вам pyCharm. Все вопросы по установке, либо возникающим проблемам Вы можете задавать в комментариях. Начиная работать, обязательно обратите внимание на pip -- установщик пакетов, который, кстати, очень хорошо интегрирован в pyCharm. Теперь насчёт изображений. В сети полно всяких дампов фоток, картинок и другого хлама. Вы легко их можете найти в гугле. Но проблема в том, что они могут быть в разных форматах. Эта проблема легко решает приведенеием их к *.png. Сделать это можно так: import Image import glob listOfImages = glob.glob("/home/hedgehogues/project/testPNG/*.jpg") # Получаем список имён файлов index = 0 for itemFile in listOfImages: img = Image.open(itemFile) img.save("/home/hedgehogues/project/testPNG/" + str(index) + ".png") index += 1 print index Код комментить не буду. Он вроде бы понятен. Итак, начал я с того, что решил поглядеть, что представляет собой размер картинки (далее картинка -- это некоторый экземпл базы картинок) в зависимости от её площади. К сожалению, здесь меня ждал грустный ответ. Это не очень хорошое распределение. Вы можете на него взглянуть: Но вообще говоря, даже глядя на такую картинку можно дать кое-какие оценки. Например, мы можем дать жёсткую оценку вёрхней границы. Она, как видно, из рисунка выражается следующим уравнением: Нижняя граница соответственно: Таким образом, размерах файлов при фиксированной площади может сильно варьироваться (практически в 3 раза) ~ 2.922. Для того, чтобы понять, что это много, можно сравнить 3 Мб и 9 Мб. 300 кБ и 1Мб. Разница ощутима. Апогея она достигнет, если рассматривать изображения очень больших размеров: 50 Мб и 150 Мб. Каково? Я приведу код, с помощью которого можно произвести рассчёты: import Image import os import matplotlib.pyplot as plt import numpy as np listOfSize = [] # Размер файла total = [] # Площадь картинки index = 0 for itemFile in range(0, 260): tmpSize = os.path.getsize("/home/hedgehogues/project/testPNG/" + str(index) + ".png") # Получаем размеры файла с картинкой img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") hLocal, wLocal = img.size # Размеры сторон картинки img = np.array(img.convert('L')) listOfSize.append(tmpSize) total.append(wLocal * hLocal) index += 1 print index plt.plot(total, listOfSize, linestyle = '', marker = 'x') plt.show() Следующим логичным шагом стало предположение о том, что, информация о длинах сторон картинок -- это совсем неинформативный признак, поскольку внутри каждой картинки кроется различная информация, а следовательно, самые информативные параметры будут связаны с интенсивностями пикселей. Здесь я оговорюсь, что далее, для простоты, все изображения я буду приводить к градациям серого. Разумеется, если этого не делать, а работать со всей информацией, то мы получим более качественные результаты. Но и сложность задачи возрастает в разы. Для учёта этой информации будем строить гистограммы изображений: Но, вот незадача, мои изображения оказываются очень большими (5000х5000) и их обработка занимает порядочное время. Поэтому я их жму. Получаю: Как видим, характер гистограммы сохраняется. Приведу код, который позволяет построить такого класса гистограммы: import Image import matplotlib.pyplot as plt import numpy as np index = 0 for itemFile in range(0, 250): img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) # сжатие изображения img = np.array(img.convert('L')) y = np.histogram(img, bins = range(0, 256)) # bins задачёт количество градаций гистограммы x = np.arange(0, 1, 1./255) plt.plot(x, y[0]) plt.show() index += 1 print index Что со всем этим делать? Легко. Можно построить аппроксимацию этих гистограмм. Это можно делать по-разному. Например, при помощи нелинейного МНК. Я подобрал такую функцию, которая более или менее отвечает гистограммам. Все гистограммы для МНК нормируются по отрезку [0; 1]. Вот, что мы получаем. Несколько гистограмм и построенных для них МНК: Данная функция подбиралась методом научного тыка и выглядит следующим образом: Средствами python легко найдём неизвестные параметры, если минимизировать будем функцию, которая соответствует МНК: Для минимизации перебёрем все данные для каждой гистограммы. Все эти действия можно проделать самостоятельно: import Image import scipy.optimize as opt import matplotlib.pyplot as plt import numpy as np # Целевая функция def Model(a, x): sum = a[0] for coeff in range(1, len(a)): sum += a[coeff] * ((x * np.sin(x)) ** coeff + np.exp(x)) return sum index = 0 for itemFile in range(0, 250): img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) img = np.array(img.convert('L')) weight = np.array(range(0, 10)) ErrorFunc = lambda tpl, x, y: 0.5 * (Model(tpl, x) - y) ** 2 # Функционал минимизации y = np.histogram(img, bins = range(0, 256)) x = np.arange(0, 1, 1./255) # Нормировка y = y[0] / float(np.max(y[0])) # Нормировка spl = opt.leastsq(ErrorFunc, weight, args = (x, y)) # Вычисление коэффициентов yy = Model(spl[0], x) plt.plot(x, yy) plt.plot(x, y) plt.show() index += 1 print index Получим веса w_i, а также имея площадь, можем построить ещё одно регрессионную модель, которая будет предсказывать размер конкртеного изображения. Сделать это можно по аналогии с тем, как построена регриссионная модель выше. Введём некоторую целевую функцию и функционал минимизации. Запишем исходные данные в виде: u_i = [w_i, area]. Теперь имея в качестве исходных данных пары (u_i, total_size), аналогичным образом обучим модель и получим некоторую зависимость. По указанной зависимости можно будет предсказывать предполагаемый размер файла. С другой стороны, можно воспользоваться более простой идеей и также, как и ранее, получить верхнюю и нижнюю оценку. Для этого посчитаем среднее значение элементов гистограммы. Построим график зависимости размера файла от среднего значения: Предвосхищая вопросы. Замечу, что на графике присутствуют два вида точек. Синие -- это множество, на котором производилось "обучение". Красные -- это точки, взятые из интернетов (картинки скачал). Как видим, они примерно укладываются в общую тенденцию. Разумеется, в данной ситуации у нас есть выбросы, которые нужно отдельно обработать и понять их причину. Кроме того, наша оценка средним значением гистограммы очень груба. А значит не следует претендовать на слишком качествеенный результат. Также отмечу, что построение гистограммы для отдельного изображения -- это затратная операция. Поэтому имеет смысл брать некотору аппроксимацию этой операции (например, брать на изображении случайные пиксели и строить гистограмму по ним). Приведу код: import Image import os import matplotlib.pyplot as plt import numpy as np def Model(a, x): sum = a[0] for coeff in range(1, len(a)): sum += a[coeff] * ((x * np.sin(x)) ** coeff + np.exp(x)) return sum listOfSize = [] listOfSizeTest = [] h = [] w = [] total = [] totalTest = [] data = [] # Перебираем все элементы из train set index = 0 for itemFile in range(0, 250): img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) img.save("/home/hedgehogues/project/testPNG/_-1.png") tmpSize = os.path.getsize("/home/hedgehogues/project/testPNG/_-1.png") img = np.array(img.convert('L')) y = np.histogram(img, bins = range(0, 256)) total.append(np.mean(y[0] / float(np.max(y[0])))) index += 1 print index # Перебираем все элементы из test set (картинки из интернетов) for itemFile in range(0, 6): img = Image.open("/home/hedgehogues/project/testPNG/_" + str(itemFile) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) img.save("/home/hedgehogues/project/testPNG/_-1.png") tmpSize = os.path.getsize("/home/hedgehogues/project/testPNG/_-1.png") img = np.array(img.convert('L')) listOfSizeTest.append(tmpSize) y = np.histogram(img, bins = range(0, 256)) totalTest.append(np.mean(y[0] / float(np.max(y[0])))) plt.plot(total, listOfSize, linestyle = '', marker = 'x') plt.plot(totalTest, listOfSizeTest, linestyle = '', marker = 'x', color = 'red') plt.show()

среда, 27 ноября 2019 г.

Лучшие алгоритмы сжатия текста

#алгоритм #сжатие


С каждым годом алгоритмы сжатия совершенствуются, появляется что-то новое, или модификация
существующих.

Вопрос: 

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

Дополнительно:


текст будет представлять себя наборы символов латиницы, кириллицы, знаков препинания
- из ASCII (cp866 или win-1251), возможно еще псевдографика будет
те же наборы символов, но представленные в кодировке ru_RU.UTF-8


Пока на слуху, но это уже относительно давно, алгоритм PPMd, PPMz. Есть что-то более
совершенное?
    


Ответы

Ответ 1



Лучший ответ на этот вопрос - спросите на форуме encode.ru. Я лично не слежу за этим совсем уж пристально, поэтому навскидку: paq8px, emma, cmix (у каждого есть своя ветка на форуме). Кроме этого, препроцессинг - словарная замена (xml-wrt), трюки Grabowski. Имейте в виду, что тексты должны быть достаточно велики (ну хотя бы сотни килобайт) и скорость сжатия/распаковки может быть в районе нескольких кб/с, а многопоточность невозможна без ухудшения сжатия. Собственно приведённая вами же ссылка http://mattmahoney.net/dc/text.html даёт достаточно исчерпывающий ответ на ваш вопрос. Все алгоритмы такого уровня получают ветки на форуме encode.ru и тестируются Маттом на тексте английской википедии. Да, язык/кодировка (при условии что она 8-битная) имеют значение только для словарных препроцессоров - они обычно работают только с латиницей. Остальные алгоритмы хорошо работают с любыми языками. Если же вам на самом деле нужно не максимальное сжатие, а оптимальное сочетание скорости и степени сжатия, то для текстов я предпочитаю bsc, тем более что это единственная библиотека сжатия общего назначения, способная использовать GPU.

Ответ 2



Попробовал сжимать "Войну и Мир" (отсюда) $ 7za a -mm=deflate -mx9 -- war_and_peace.txt.deflate.7z $ 7za a -mm=bzip2 -mx9 -- war_and_peace.txt.bz2.7z war_and_peace.txt $ 7za a -mm=lzma2 -mx9 -- war_and_peace.txt.lzma2.7z war_and_peace.txt $ 7za a -mm=ppmd -mx9 -- war_and_peace.txt.ppmd.7z war_and_peace.txt $ dir 1 473 547 war_and_peace.txt 577 577 war_and_peace.txt.deflate.7z 481 030 war_and_peace.txt.lzma2.7z 445 240 war_and_peace.txt.bz2.7z 391 270 war_and_peace.txt.ppmd.7z Видно, что PPMd самый эффективный на "Войне и Мире". За ним идет BZ2, потом LZMA2 и Deflate. Попробовал cmix. Он действительно жрет 36 Гб памяти и сжимал 43 минуты. $ cmix -c war_and_peace.txt war_and_peace.txt.cmix 1473547 bytes -> 348461 bytes in 2633.81 s. cross entropy: 1.892 У него еще есть режим со словарем (орфоргафическим), но в комплекте идет только словарь из ~45 тыс. английских слов. Впрочем это довольно старые и широко известные алгоримы. Возможно существует что-то другое, более подходящее для текстов которые надо сживать автору вопроса. Мне нечего было делать, и я разбил "Войну и Мир" по словам и закодировал их Хаффманом. Получилось 444Кб не считая таблицу. Т.е. это была плохая идея. >>> text = open(r'c:\_tmp\war_and_peace.txt', 'rb').read().decode('cp1251') >>> import re >>> words = re.findall(r'(\w+|.)', text) >>> len(text), len(words) (1473547, 534395) >>> def huffman_table(symbols): import collections freq = collections.defaultdict(int) for s in symbols: freq[s] += 1 roots = [(f,[(s,'')]) for s,f in freq.items()] while True: roots.sort() (f0,t0),(f1,t1),tail=roots[0],roots[1],roots[2:] table=[(s,'0'+c) for s,c in t0]+[(s,'1'+c) for s,c in t1] if len(tail) == 0: return dict(table) roots = tail+[(f0+f1,table)] >>> table = huffman_table(words) >>> len(table) 35421 >>> def huffman_encode(symbols, table): buffer = '' out = bytearray() for s in symbols: buffer += table[s] while len(buffer) >= 8: b, buffer = buffer[:8], buffer[8:] out.append(int(b, 2)) size = len(out)*8+len(buffer) if len(buffer) != 0: out.append(int(buffer, 2)) return out, size >>> encoded, size_bits = huffman_encode(words, table) >>> size_bits/8 454533.75

понедельник, 12 ноября 2018 г.

Как скомпрессировать набор натуральных чисел? Порядок неважен, повторов нет.

Задача сравнить два набора уникальных целых положительных чисел, и найти присутствующие сразу в обоих. Все точно лежат в диапазоне от 1 до 200 млн. Обычно в каждом из двух наборов от 0 до 5 млн чисел. До сих пор делаю "в лоб": оба сета заношу во временные таблицы MySQL. Две одноколоночные таблицы, где числа – первичные ключи. Сравнение проходит быстро, если сеты маленькие и помещаются в engine=MEMORY. Медленно, когда таблицы большие и приходится создавать их на диске. Когда надо таких сравнений выполнять помногу и часто — тормоза. Что, если воспроизвести индексированные колонки MySQL в собственном коде? Один из сетов держать в памяти, а каждый элемент второго проверять на наличие в первом. Не хранить каждое из чисел набора (32бит, 2.5млн в среднем = 80Мб), а работать с битовой маской всех возможных значений. 200 млн это, с запасом, 2^28 = 268,435,456 бит = 32Мб. Установлен – число есть в наборе, 0 – нет. Сравнивать установленные биты. В полном виде хранить для каждого сета весь набор битов неэффективно. Наверняка, можно такие данные здорово компрессировать. Большинство битов будут 0, значит, их последовательности можно кодировать их кол-вом подряд например. Вопрос к такому компрессированному массиву будет один: есть ли очередное искомое число в наборе, или нет? Упростим для примера. Пусть всего может быть 32 значения: 0..31. Наш массив будет состоять из 32 нулей/единиц. В наборе присутствуют всего два значения: 17 и 22. 16 нулей, единица, 4 нуля, 1. И запишем их как 16,4: 10000100. Всего 8 бит вместо 2*6. компрессия сэкономила 25%. Но это моё совсем косолапое представление о возможном способе компрессии, без разделителей, единиц подряд и т.п. Надо узнать про число 19, есть ли в наборе? Проходим по нашим 8 битам: 16 ещё пока меньше 19, ещё 4 — уже перебор, ответ "нет в наборе". Как по-вашему, есть ли вообще смысл в таком велосипеде, может ли он ускорить сравнение двух сетов, по сравнению с MySQL? Upd. Проще сформулирую вопрос. Ищется компрессия для данных, когда известны их параметры и ограничения: только натуральные числа от .. до .., не подряд, не сортированные, без повторов, порядок неважен. И даже без необходимости распаковки: нужно лишь уметь ответить на вопрос «есть ли такое-то число в наборе, или нет?».


Ответ

1) БД не для решения таких задач 2) используем либо упорядоченные списки, либо хештаблицы 3) я бы использовал упорядоченный список или массив, и потом сравнивал методом попарного слияния: - оба указателя на первые элементы берем элемент с первого списка, сравниваем с элементом второго списка если значения равны, то элемен заносим в список результата и увеличиваем оба указателя и к пп 1 если 1 < 2, увеличиваем значение указателя первого списка иначе второго итого, используемые алгоритмы: 2 * qsort и попарного слияния если списки ну очень большие...(20 млн вполне терпимо для памяти, но 200 может быть уже перебором), то их можно разбить на части и сравнивать сперва часть 1, потом часть 2. Алгоритм приблизительно следующий: есть списки 1: А+Б+С+Д... и 2:А+Б+С+Д... - сравниваем список 1А и 2А если список 1А закончился, то сравниваем конец списка 2А и список 1Б иначе конец списка 2Б со списоком 1А как только какой список заканчивается текущим становится следующий из данной последовательности (1 или 2 ) и так далее

четверг, 18 октября 2018 г.

Можно ли предсказать максимальный размер png изображения?

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


Ответ

Вы задали сложный и интересный вопрос, точного ответа на который скорее всего ни у кого нет. Хотя Вы можете поискать какие-нибудь оценки алгоритма сжатия DEFLATE, который используется в PNG. К слову, о PNG. Это формат хранения картинок со сжатием данных без потерь, в котором используется алгоритм DEFLATE. Метод не из самых простых. Его достаточно хорошее описание мной найдено было найдено здесь, здесь, частично тут и вот тут. Но в силу того, что мои интересы лежат немного в стороне от сжатия данных, вспоминать коды Хаффмана в различных вариациях я не стал, а избрал иной, более дешёвый подход к решению задачи. Он даёт худший результат и не точный результат. Но, при должном развитии идеи, которую я опишу, можно получить достаточно качественные нижнюю и верхнюю оценки. Нужно также понимать, что используюя данный подход, всегда можно будет подобрать такое изображению, которое будет выбиваться из общей динамики картинок. В силу того, что я не знаю, что и как Вы хотите оптимизировать, приведу лишь основные положения того, что можно делать. Всё ниже перечисленное снабжу кодом на python 2.7. Готового решения, к сожалению, я не дам, посколько не знаю целей. Но на основе моих наработок можно получить очень хороший результат.
Насчёт точной оценки. Её скорее всего можно получить, изучив то, как ведут себя коды Хаффмана. Но это будет трудоёмкий процесс. Вопрос в том, насколько это Вам нужно.
Общая идея подхода состоит в том, чтобы выявить зависимость
, где
-- параметры, которые будем задавать. Указанную зависимость мы будем получать, рассматривая некоторое множество изображений (у меня на компьютере есть база со стоковыми фотографиями профессиональных фотографов). Эти изображения мы будем рассматривать как объекты. Выше указанную зависимость мы будем строить, основываясь на выборке картинок.
Если у Вас нет python и IDE для него, то я советую Вам pyCharm. Все вопросы по установке, либо возникающим проблемам Вы можете задавать в комментариях. Начиная работать, обязательно обратите внимание на pip -- установщик пакетов, который, кстати, очень хорошо интегрирован в pyCharm
Теперь насчёт изображений. В сети полно всяких дампов фоток, картинок и другого хлама. Вы легко их можете найти в гугле. Но проблема в том, что они могут быть в разных форматах. Эта проблема легко решает приведенеием их к *.png. Сделать это можно так:
import Image import glob
listOfImages = glob.glob("/home/hedgehogues/project/testPNG/*.jpg") # Получаем список имён файлов
index = 0 for itemFile in listOfImages: img = Image.open(itemFile) img.save("/home/hedgehogues/project/testPNG/" + str(index) + ".png") index += 1 print index
Код комментить не буду. Он вроде бы понятен.
Итак, начал я с того, что решил поглядеть, что представляет собой размер картинки (далее картинка -- это некоторый экземпл базы картинок) в зависимости от её площади. К сожалению, здесь меня ждал грустный ответ. Это не очень хорошое распределение. Вы можете на него взглянуть:

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

Нижняя граница соответственно:

Таким образом, размерах файлов при фиксированной площади может сильно варьироваться (практически в 3 раза) ~ 2.922. Для того, чтобы понять, что это много, можно сравнить 3 Мб и 9 Мб. 300 кБ и 1Мб. Разница ощутима. Апогея она достигнет, если рассматривать изображения очень больших размеров: 50 Мб и 150 Мб. Каково?
Я приведу код, с помощью которого можно произвести рассчёты:
import Image import os import matplotlib.pyplot as plt import numpy as np
listOfSize = [] # Размер файла total = [] # Площадь картинки
index = 0 for itemFile in range(0, 260): tmpSize = os.path.getsize("/home/hedgehogues/project/testPNG/" + str(index) + ".png") # Получаем размеры файла с картинкой img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") hLocal, wLocal = img.size # Размеры сторон картинки img = np.array(img.convert('L')) listOfSize.append(tmpSize) total.append(wLocal * hLocal) index += 1 print index
plt.plot(total, listOfSize, linestyle = '', marker = 'x') plt.show()
Следующим логичным шагом стало предположение о том, что, информация о длинах сторон картинок -- это совсем неинформативный признак, поскольку внутри каждой картинки кроется различная информация, а следовательно, самые информативные параметры будут связаны с интенсивностями пикселей. Здесь я оговорюсь, что далее, для простоты, все изображения я буду приводить к градациям серого. Разумеется, если этого не делать, а работать со всей информацией, то мы получим более качественные результаты. Но и сложность задачи возрастает в разы.
Для учёта этой информации будем строить гистограммы изображений:

Но, вот незадача, мои изображения оказываются очень большими (5000х5000) и их обработка занимает порядочное время. Поэтому я их жму. Получаю:

Как видим, характер гистограммы сохраняется. Приведу код, который позволяет построить такого класса гистограммы:
import Image import matplotlib.pyplot as plt import numpy as np
index = 0 for itemFile in range(0, 250): img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) # сжатие изображения img = np.array(img.convert('L')) y = np.histogram(img, bins = range(0, 256)) # bins задачёт количество градаций гистограммы x = np.arange(0, 1, 1./255) plt.plot(x, y[0]) plt.show() index += 1

print index
Что со всем этим делать? Легко. Можно построить аппроксимацию этих гистограмм. Это можно делать по-разному. Например, при помощи нелинейного МНК. Я подобрал такую функцию, которая более или менее отвечает гистограммам. Все гистограммы для МНК нормируются по отрезку [0; 1]. Вот, что мы получаем. Несколько гистограмм и построенных для них МНК:

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

Средствами python легко найдём неизвестные параметры, если минимизировать будем функцию, которая соответствует МНК:

Для минимизации перебёрем все данные для каждой гистограммы. Все эти действия можно проделать самостоятельно:
import Image import scipy.optimize as opt import matplotlib.pyplot as plt import numpy as np
# Целевая функция def Model(a, x): sum = a[0] for coeff in range(1, len(a)): sum += a[coeff] * ((x * np.sin(x)) ** coeff + np.exp(x)) return sum

index = 0 for itemFile in range(0, 250): img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) img = np.array(img.convert('L')) weight = np.array(range(0, 10)) ErrorFunc = lambda tpl, x, y: 0.5 * (Model(tpl, x) - y) ** 2 # Функционал минимизации y = np.histogram(img, bins = range(0, 256)) x = np.arange(0, 1, 1./255) # Нормировка y = y[0] / float(np.max(y[0])) # Нормировка spl = opt.leastsq(ErrorFunc, weight, args = (x, y)) # Вычисление коэффициентов yy = Model(spl[0], x) plt.plot(x, yy) plt.plot(x, y) plt.show() index += 1

print index
Получим веса w_i, а также имея площадь, можем построить ещё одно регрессионную модель, которая будет предсказывать размер конкртеного изображения. Сделать это можно по аналогии с тем, как построена регриссионная модель выше. Введём некоторую целевую функцию и функционал минимизации. Запишем исходные данные в виде: u_i = [w_i, area]. Теперь имея в качестве исходных данных пары (u_i, total_size), аналогичным образом обучим модель и получим некоторую зависимость. По указанной зависимости можно будет предсказывать предполагаемый размер файла.
С другой стороны, можно воспользоваться более простой идеей и также, как и ранее, получить верхнюю и нижнюю оценку. Для этого посчитаем среднее значение элементов гистограммы. Построим график зависимости размера файла от среднего значения:

Предвосхищая вопросы. Замечу, что на графике присутствуют два вида точек. Синие -- это множество, на котором производилось "обучение". Красные -- это точки, взятые из интернетов (картинки скачал). Как видим, они примерно укладываются в общую тенденцию. Разумеется, в данной ситуации у нас есть выбросы, которые нужно отдельно обработать и понять их причину. Кроме того, наша оценка средним значением гистограммы очень груба. А значит не следует претендовать на слишком качествеенный результат. Также отмечу, что построение гистограммы для отдельного изображения -- это затратная операция. Поэтому имеет смысл брать некотору аппроксимацию этой операции (например, брать на изображении случайные пиксели и строить гистограмму по ним).
Приведу код:
import Image import os import matplotlib.pyplot as plt import numpy as np

def Model(a, x): sum = a[0] for coeff in range(1, len(a)): sum += a[coeff] * ((x * np.sin(x)) ** coeff + np.exp(x)) return sum
listOfSize = [] listOfSizeTest = [] h = [] w = [] total = [] totalTest = [] data = []
# Перебираем все элементы из train set index = 0 for itemFile in range(0, 250): img = Image.open("/home/hedgehogues/project/testPNG/" + str(index) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) img.save("/home/hedgehogues/project/testPNG/_-1.png") tmpSize = os.path.getsize("/home/hedgehogues/project/testPNG/_-1.png") img = np.array(img.convert('L')) y = np.histogram(img, bins = range(0, 256)) total.append(np.mean(y[0] / float(np.max(y[0])))) index += 1

print index
# Перебираем все элементы из test set (картинки из интернетов) for itemFile in range(0, 6):
img = Image.open("/home/hedgehogues/project/testPNG/_" + str(itemFile) + ".png") img.thumbnail((300, 300), Image.ANTIALIAS) img.save("/home/hedgehogues/project/testPNG/_-1.png") tmpSize = os.path.getsize("/home/hedgehogues/project/testPNG/_-1.png") img = np.array(img.convert('L')) listOfSizeTest.append(tmpSize) y = np.histogram(img, bins = range(0, 256)) totalTest.append(np.mean(y[0] / float(np.max(y[0]))))
plt.plot(total, listOfSize, linestyle = '', marker = 'x') plt.plot(totalTest, listOfSizeTest, linestyle = '', marker = 'x', color = 'red') plt.show()

среда, 3 октября 2018 г.

Лучшие алгоритмы сжатия текста

С каждым годом алгоритмы сжатия совершенствуются, появляется что-то новое, или модификация существующих.
Вопрос:
Какие из ныне существующих, на 2016 год, алгоритмов сжатия текстовой информации дают лучший результат (естественно, без потерь)?
Дополнительно:
текст будет представлять себя наборы символов латиницы, кириллицы, знаков препинания - из ASCII (cp866 или win-1251), возможно еще псевдографика будет те же наборы символов, но представленные в кодировке ru_RU.UTF-8
Пока на слуху, но это уже относительно давно, алгоритм PPMd, PPMz. Есть что-то более совершенное?


Ответ

Лучший ответ на этот вопрос - спросите на форуме encode.ru. Я лично не слежу за этим совсем уж пристально, поэтому навскидку: paq8px, emma, cmix (у каждого есть своя ветка на форуме). Кроме этого, препроцессинг - словарная замена (xml-wrt), трюки Grabowski. Имейте в виду, что тексты должны быть достаточно велики (ну хотя бы сотни килобайт) и скорость сжатия/распаковки может быть в районе нескольких кб/с, а многопоточность невозможна без ухудшения сжатия.
Собственно приведённая вами же ссылка http://mattmahoney.net/dc/text.html даёт достаточно исчерпывающий ответ на ваш вопрос. Все алгоритмы такого уровня получают ветки на форуме encode.ru и тестируются Маттом на тексте английской википедии.
Да, язык/кодировка (при условии что она 8-битная) имеют значение только для словарных препроцессоров - они обычно работают только с латиницей. Остальные алгоритмы хорошо работают с любыми языками.
Если же вам на самом деле нужно не максимальное сжатие, а оптимальное сочетание скорости и степени сжатия, то для текстов я предпочитаю bsc, тем более что это единственная библиотека сжатия общего назначения, способная использовать GPU.