ресайза изображения? Я нашёл только то, что надо отключить кеширование операций, иначе скорость ресайза улетает в бесконечность.
Но, очевидно, это не всё, т.к. у меня получается так что уменьшение картинки с фильтром lancsoz3 выполняется всего лишь в 4 раза медленнее чем nearest.
Такого просто не может быть в однопоточном режиме.
почему не может быть?
nearest - это никаких вычислений. В общем случае - это просто копирование байтиков с заданным шагом. А свёртка с адаптивным размером ядра - это вычисление коэффициентов и потом умножение каждого пикселя исходного изображения по нескольку раз на разные коэффициенты. Это не может быть медленнее всего лишь в 2-4 раза чем простейшее копирование
Я думаю может, потому что ядро применяется локально
А на флеймграфе не будет видно, что отнимает у libvips много времени?
Судя по результату - ядро в libvips с адаптивным размером. Т.е. все пиксели исходного изображения учавствуют в формировании результата. Я сам на расте делаю аналогичный ресайзер с использованием AVX2 инструкций. Даже он не выдаёт таких волшебных результатов.
Так дело в том что не много, а очень мало. Пожалуй проверю не научным способом - измеряю время одного запуска ресайза через libvips, вместо 100500 запусков с последующим стат.анализом
Основные тормоза это чтение и запись данных, умножение на ядро сейчас быстрое, а при копировании ты читаешь не сильно меньше чем свёртка на ядро
Ну так в том и дело, что при nearest надо прочитать и записать только часть пикселей, а при свёртке с адаптивным ядром надо прочитать все пиксели исходного изображения. Ещё и по несколько раз.
Вы читаете не байт, а строку кеша, т.е. по факту все изображение
Обычно там делается в два прохода. Сначала ресайзят по горизонтали, потом по вертикали (или наоборот). Т.е. тут мало того что два раза надо читать (при этом по вертикали приходится читать не подряд, а с шагом в размер строки), так ещё и надо выполнить доп. запись первого прохода во временный буфер.
А зачем? Что мешает за один проход делать горизонтальное вертикальное
В регистр сразу добавлять свертки
Можно конечно попробовать в один проход сделать, но тогда придётся вычитывать всю матрицу пикселей, которые сворачиваются в результирующий один пиксель. И эта "матрица" расположена в исходном изображении не последовательно. После чего умножать матрицу на все коэффициенты ядра, суммировать и записывать результат. После чего вычитывать матрицу для следующего пикселя, заменить в ядре набор коэффициентов (горизонтальный или вертикальный, в зависимости от того какой следующий пиксель взяли), который тоже лежит в памяти (ок, он в кеш влезает, но всё равно надо читать в регистры). При ресайзе очень большого изображения в очень маленькое размер матрицы может получиться очень большой, и ядро тоже будет очень большое. А значит целиком всё не влезет в регистры даже от AVX2. Придётся по несколько раз передёргивать данные из памяти в регистры. Потому и делают в два прохода, т.к. требуется меньше операций загрузки данных в SIMD регистры, как минимум для коэффициентов. Т.к., например, для сжатия по горизонтали, коэффициенты не меняются для одного столбца пикселей, т.е. можно прочитать в регистры пиксели из 2-4 соседних строк изображения и умножить их 1-2 командами на уже загруженные в регистры коэффициенты. В общем видимо два прохода оптимальнее для использования SIMD. Но это не я придумал - я честно украл это из Pillow-SIMD, про которую есть статья на хабре. В данный момент только libvips опережает эту реализацию по скорости в однопоточном режиме. Но делает он это слишком волшебно, я пока не разобрался как именно - очень запутанный у него код.
На каком соотношение разрешений он быст?
Сейчас я уменьшаю картинку условно 5000px в 250px по одной стороне. Уменьшаю примерно в 20 раз. При таком раскладе libipv в один поток выдаёт у меня такие результаты (округлил до целых мс) nearest linear cubic lancsoz3 1 2 3 4 моя либа на тех же данных выдаёт вот такое 1 20 30 60
Бегло посмотрел по чату. Понял что вы используете AVX. Для быстрого сжатия картинок он слишком медленный. У меня подобная проблема была. Решил через gpu
А зачем при таком сжатии изображения вообще сложные методы? Просто усреднение не пойдет?
У компании нет денег на сервера с GPU 😊 Либа в первую очередь делается для ресайза картинок в веб-сервисе.
Разбей массив по ядрам. В каждом ядре вроде как свои регистры avx. Может поможет
Что мешает брать просто среднее по кускам?
Ну это по сути интерполяция.
При таком сжатии это вполне норм
Ну да, это стрёмная недо-интерполяция. Результат у неё будет так себе, т.к. при среднем будет получаться что дальние от требуемого пиксели оказывают такой же вклад в результат, как и самые ближние.
При сжатии в 20 раз это не важно
В общем такой фильтр называется box - он у меня в либе тоже есть. Вопрос то был не про то что мне лучше использовать для ресайза, а про то мочему libvips такой волшебный в бенчмарках.
Ну при таком коэффициенте он может просто делать бокс не сильно ошибаясь
А сколько ядер в процессоре на котором делался тест ?
Да, box при таких масштабах просто выдаёт более мыльную картинку чем cubic или lancsoz3. Мелкие детали замыливаются, а в остальном вполне прилично.
Если хорошо то смотри к gtpchat
Вы читали Дреппера? У него хитрый обход матрицы в один проход.
Хм, а в libvips нет box фильтра. Не получится делать бенчмарки адекватные для сравнения с моей либой 😞
Ааа, так и знал, что libvips "волшебный". У него ленивые вычисления используются. Он не выполняет над изображением ни каких операций, пока не понадобится доступ к пикселям этого изображения. Поэтому у меня бенчмарк и показывал офигительные цифры. Я добавил "применение" опреации ресайза и всё встало на свои места и моя либа подтвердила своё лидерство в однопоточном dawnscale-е картинок. Капец - nearest в libvips выполняется прям супер медленно. А с box-ом что-то чудесатое в нём случается - медленнее чем lancsoz3 в 2 раза. Время в таблице в миллисекундах. | | Nearest | |----------|:-------:| | libvips | 21.64 | | fir avx2 | 0.86 | | | Box | Linear | |----------|:------:|:------:| | libvips | 193.67 | 51.93 | | fir avx2 | 20.23 | 21.74 | | | Cubic | Lanczos3 | |----------|:-----:|:--------:| | libvips | 78.48 | 104.58 | | fir avx2 | 28.14 | 41.13 |
Если libvips разрешить юзать 8 потоков, то он становится шустрее, хотя nearest и box всё равно в какой-то заднице
Кто такой этот ваш мистер Дреппер?
Ооо, а если надо ресайзить картинку с учётом альфа-канала, то libvips превращается в самую медленную кобылу (в однопоточном режиме). У него, может быть, точность вычислений будет лучше из-за того, что при умножении и делении на альфа-канал он результат сохраняет в f32 на каждую компоненту пикселя, а не в u8/u16. Но скорость из-за этого страдает катастрофически.
Обсуждают сегодня