awxkee / avif-coder

AVIF/HEIC coder for Android, Kotlin, Java
MIT License
65 stars 7 forks source link

FFmpeg libswscale #14

Closed T8RIN closed 10 months ago

T8RIN commented 10 months ago

Hi, can you implement bitmap scaling based on ffmpeg libswscale, because it can choose between some algorhytms, like bicubic, bilenear, lancoz and others, if you can here is abandoned project that support only two archs and not starts on api 24 and abov

awxkee commented 10 months ago

Hi, can you implement bitmap scaling based on ffmpeg libswscale, because it can choose between some algorhytms, like bicubic, bilenear, lancoz and others, if you can here is abandoned project that support only two archs and not starts on api 24 and abov

Hello there, The project as default using a provided libheif scaling when it needs to be downscaled that is bicubic algorithm. Not really sure that is good idea trying to compile part of FFmpeg for this one. In jxl-coder you may found a class XScaler.cpp that contains already done scaling for float16 bitmap format and argb 8-bit with ready to go algorithms - nearest neighbor, bcubic, bilinear, Mitchell-Netravalli, Catmull Rom, lanczos and hann window and hermite spline and some math tricks like fast sin/cos via chebyshev polynoms and etc. And I may say that algorithms not worse than libswscale especially for arm64 platforms.

If you are thinking about dedicated library, I'm not really into it, but you may easily find all the required code in my projects.

I may say you before you start that default android scaling is bilinear was chosen for a reason. It has awesome performance on a not quite powerful devices and do especially well when you need a scale that not changing image dimensions too much. There are not so many real world cases an android when you need a Spline that do really well when you need a scale more than 2x or original dimensions and have 2+ times performance degradation against bilinear. And also not so much cases when you need a Lanczos that do well on scale like 0.1x of original dimensions and require computational power at least 3-4 times more than bilinear

T8RIN commented 10 months ago

Hm, thanks for pointing to XScale.cpp , didn't know that it exists in your code already, but as you know i'm noob at ndk, so how can i connect this scaler with bitmaps? Like i can just use BitmapScaler.scale(bitmap, width, height, algo) and here we go

awxkee commented 10 months ago

For this function you'll need in NDK. Use AndroidBitmap_getInfo to get bitmap info, use AndroidBitmap_lockPixels to get bitmap pixels in your memory buffer ( not forget to AndroidBitmap_unlockPixels after), then create a new memory buffer where scale to, pass both buffer to a scaler, create a new JVM bitmap object and copy your destination buffer to a new bitmap also using AndroidBitmap_lockPixels and AndroidBitmap_unlockPixels

T8RIN commented 10 months ago

For this function you'll need in NDK. Use AndroidBitmap_getInfo to get bitmap info, use AndroidBitmap_lockPixels to get bitmap pixels in your memory buffer ( not forget to AndroidBitmap_unlockPixels after), then create a new memory buffer where scale to, pass both buffer to a scaler, create a new JVM bitmap object and copy your destination buffer to a new bitmap also using AndroidBitmap_lockPixels and AndroidBitmap_unlockPixels

Thanks for this explanations, i'll try and ask if something will not work

T8RIN commented 10 months ago

image Can you explain please what is components and stride's are?

awxkee commented 10 months ago

Components is the count of color components in the image, surprisingly but RGB will have 3 components and Rgba have 4 components. Stride is a row length of the image, you may thinks that it’s equal to width × components. × (size of component) but it often doesn’t due to optimization trick, because L1 cache of the processor is aligned at least to 64 bytes most of libraries will try to round your real row length ‘width × compents ×(size of compents)’ to be dividable for 64 ( or anything else value, it depends) and just ignore this bytes after. So you have to ignore this aligned bytes, but have to consider that really allocated memory for the image row can be a little bigger than image row really is

T8RIN commented 10 months ago

so i can provide as stride ‘width × compents ×(size of compents)’ and not think about rounding?

awxkee commented 10 months ago

You have to provide stride that really is, stride of the original bitmap you may obtain from bitmap info. For your destination buffer yes, you may allocate bytes for exact row size

T8RIN commented 10 months ago

Thanks!

awxkee commented 10 months ago

Когда закончишь, я, впринципе, могу взглянуть нормально ли все у тебя вышло

T8RIN commented 10 months ago

Хорошо, а ты русский знаешь что ли? 😳

awxkee commented 10 months ago

Да, это ж не секретный язык )

T8RIN commented 10 months ago

Ты русский чтоль? Явно не через переводчик это пишешь))

awxkee commented 10 months ago

Хотя я бы рекомендовал еще раз взвесить стоит ли это того, т.к. когда ты через coil / glide грузишь картинки, они уже пересжаты, и то что ты добавишь более продвинутый алгоритм на пересжатие еще раз врятли даст действительно стоящий результат

awxkee commented 10 months ago

Я беларус

T8RIN commented 10 months ago

Я беларус

Пипец, а живешь не там, да?

awxkee commented 10 months ago

Не, в беларуси я 3 года уже не живу

T8RIN commented 10 months ago

Хотя я бы рекомендовал еще раз взвесить стоит ли это того, т.к. когда ты через coil / glide грузишь картинки, они уже пересжаты, и то что ты добавишь более продвинутый алгоритм на пересжатие еще раз врятли даст действительно стоящий результат

У меня ж для работы с картинками прога, я изначально гружу картинку в оригинальном разрешении, и можно ее сжать/увеличить, так что норм должно быть, попросили прост чет кроме билинейного скейлинга добавить на выбор

T8RIN commented 10 months ago

Не, в беларуси я 3 года уже не живу

По работе переехал чтоль?

awxkee commented 10 months ago

Не, в беларуси я 3 года уже не живу

По работе переехал чтоль?

Я враг лукашенки, не переехал, а убежал )

Не может быть чтоб ты оригинальные грузил, тогда у тебя картинки с любого флагмана должны просто крашиться т.к. они очень большие

awxkee commented 10 months ago

Если ты специально не продумывал загрузку оригиналов - у тебя либо креши на больших фото, либо они уже сжаты

T8RIN commented 10 months ago

Если ты специально не продумывал загрузку оригиналов - у тебя либо креши на больших фото, либо они уже сжаты

Нене, я показываю в качестве превью засемпленный вариант, а когда чел сохраняет, то я полноразмерную битмапу гружу, но потому что я ее не показываю, то не крешится, хотя конечно если 12к*12к и выше то может крешнуться, вот тут ваще хз как убрать это ограничение андроида, ток если все добро на ндк перенести, но я больше по котлину чем по плюсам 🐸

awxkee commented 10 months ago

Даже в самом "оптимистичном" случае если тебя пропустила JVM и ты загрузил картинку как HARDWARE андроид не допускает отрисовки картинки больше чем 104МБ в памяти, это RGBA 16bit максимум 3.6kx3.6k, или 8bit умножь размер RGBA на 2

awxkee commented 10 months ago

Если ты специально не продумывал загрузку оригиналов - у тебя либо креши на больших фото, либо они уже сжаты

Нене, я показываю в качестве превью засемпленный вариант, а когда чел сохраняет, то я полноразмерную битмапу гружу, но потому что я ее не показываю, то не крешится, хотя конечно если 12к*12к и выше то может крешнуться, вот тут ваще хз как убрать это ограничение андроида, ток если все добро на ндк перенести, но я больше по котлину чем по плюсам 🐸

Я тебе кажется писал уже, только сохранять все твои преобразования на превью, и потом трансформировать их в оригинал в NDK. Либо искуственно обрезать картинку чтобы она никогда не была больше 100МБ

T8RIN commented 10 months ago

Даже в самом "оптимистичном" случае если тебя пропустила JVM и ты загрузил картинку как HARDWARE андроид не допускает отрисовки картинки больше чем 104МБ в памяти, это RGBA 16bit максимум 3.6kx3.6k, или 8bit умножь размер RGBA на 2

А вроде у меня запрет на хардвер битмапы стоит

awxkee commented 10 months ago

Если ты специально не продумывал загрузку оригиналов - у тебя либо креши на больших фото, либо они уже сжаты

Нене, я показываю в качестве превью засемпленный вариант, а когда чел сохраняет, то я полноразмерную битмапу гружу, но потому что я ее не показываю, то не крешится, хотя конечно если 12к*12к и выше то может крешнуться, вот тут ваще хз как убрать это ограничение андроида, ток если все добро на ндк перенести, но я больше по котлину чем по плюсам 🐸

Если так то - делай, правда все равно билинейный алгоритм самый оптимальный, ланцош хорош только когда ты уменьшаешь больше чем на 30-40%, а сплайны только когда картинка увеличивается хотя бы на 40%

T8RIN commented 10 months ago

Спокойно пережимает 10к на 10к, можешь щас чекнуть даж👀

T8RIN commented 10 months ago

Если ты специально не продумывал загрузку оригиналов - у тебя либо креши на больших фото, либо они уже сжаты

Нене, я показываю в качестве превью засемпленный вариант, а когда чел сохраняет, то я полноразмерную битмапу гружу, но потому что я ее не показываю, то не крешится, хотя конечно если 12к*12к и выше то может крешнуться, вот тут ваще хз как убрать это ограничение андроида, ток если все добро на ндк перенести, но я больше по котлину чем по плюсам 🐸

Я тебе кажется писал уже, только сохранять все твои преобразования на превью, и потом трансформировать их в оригинал в NDK. Либо искуственно обрезать картинку чтобы она никогда не была больше 100МБ

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

T8RIN commented 10 months ago

С ндк я ваще не тютю, так что не могу сделать все там(

awxkee commented 10 months ago

И правда жмет, только поворот на больших картинках длиться по пол минуты, и оно дает во время поворота сохранять и 16 битные картинки стали 8 бит

awxkee commented 10 months ago

С ндк я ваще не тютю, так что не могу сделать все там(

Пробуй, надо с чего-то начинать, базовый вариант скейла достаточно простой только для 8 бит ARGB

T8RIN commented 10 months ago

И правда жмет, только поворот на больших картинках длиться по пол минуты, и оно дает во время поворота сохранять и 16 битные картинки стали 8 бит

Вот кста про 16 бит и 8 я хз, по-моему у меня это не предусмотрено, а че там надо в конструктор битмапы передавать, чтобы битность указать ARGB8888 это 32 бит ж ваще

awxkee commented 10 months ago

Вот картинка для теста, https://backup-csh.fra1.digitaloceanspaces.com/avif10bit%20(2).avif 10 бит, должно быть 16 на андроиде или 10бит для 34+ можно, ее поворачивает крайне долго

T8RIN commented 10 months ago

С ндк я ваще не тютю, так что не могу сделать все там(

Пробуй, надо с чего-то начинать, базовый вариант скейла достаточно простой только для 8 бит ARGB

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

T8RIN commented 10 months ago

Вот картинка для теста, https://backup-csh.fra1.digitaloceanspaces.com/avif10bit%20(2).avif 10 бит, должно быть 16 на андроиде или 10бит для 34+ можно, ее поворачивает крайне долго

Пипец конечно, я ваще изначально прогу для себя делал, но чет стрельнула

awxkee commented 10 months ago

И правда жмет, только поворот на больших картинках длиться по пол минуты, и оно дает во время поворота сохранять и 16 битные картинки стали 8 бит

Вот кста про 16 бит и 8 я хз, по-моему у меня это не предусмотрено, а че там надо в конструктор битмапы передавать, чтобы битность указать ARGB8888 это 32 бит ж ваще

Bitmap.Config.RGBA_F16 для получения 16 бит картинок где тип данных half float, флоат не должно тебя вводить в заблуждение, это не float, а другой тип данных, у меня в проектах есть half.hpp класс где есть все что для него нужно в NDK, и в проектах примеры, работает на андроиде толи с 26,+ толи с 28+, памяти нужно ровно в 2 раза больше чем для 8bit, этот тип необходимо без вариантов использовать для HDR фото, как фото выше, для отображения

RGBA_1010102 доступен с 34+ или 33+, это 10bit R канал, 10bit канал, 10 - B канал, и 2 бита на A, соответсвенно а может быть равно только 0, 1, 2, 3, это хорошо подходит когда у тебя нет альфа каначала и фото больше 8бит, изображение соотвественно будет считаться 10 битным, но памяти нужно столько же сколько для ARGB 8

T8RIN commented 10 months ago

Так, окей, а зачем тогда 32 битная вариация есть?

awxkee commented 10 months ago

32 битная? Я о такой не слышал на телефоне, ну в теории можно в JXL какую угодно глубину цвета вбить, типа карты геоместности ( там карты глубин ) или сьемки космоса ( там тоже соотвественно карты глубин ), но это уже какая-то достаточно научная штука, не думаю что она нужна

T8RIN commented 10 months ago

ARGB_8888 в конфиге как раз

T8RIN commented 10 months ago

По-моему как будто по дефолту даж она стоит, или я не понимаю чет и это 8 бит?👀

awxkee commented 10 months ago

это 8 бит, не 32, битность изображения считается по размеру компонента, а не точке

awxkee commented 10 months ago

A - это компонент, ARGB - это цветовая точка, размерность изображения считается по компоненте, не по точке

T8RIN commented 10 months ago

А, ну тогда понятно че битность теряется, пофикшу

T8RIN commented 10 months ago

А кстати, если у картинки изначально было 8 бит, я же не пойму это, так как по дефолту буду грузить больше бит чем надо, и сохранять по сути тоже, тип если я 8 битную сохраню как 16 бит, то размер не увеличится?

awxkee commented 10 months ago

В основном, если ты 8 бит сохранишь как 10, 12, 16 размер увеличится, насколько- зависит от кодека

Можно проверить изначальный bitmap config или взять инфу о картинке и из нее битность, скорее всего в Андроиде есть готовое апи

awxkee commented 10 months ago

Есть еще опция можешь на сам сделать скейл на Vulkan или OpenGL, вероятно ты найдешь такие предложения, для огромных картинок он будет в 2-3 раза быстрее чем в на c++, только надо апи 26+ вроде, научиться писать шейдеры и доступ к Vulkan все равно только с c++, он будет действительно быстрый, но из-за архи сложной конфигурации Vulkan и OpenGL и если ты не знаком с шейдерами- не рекомендую

awxkee commented 10 months ago

Хотя OpenGL можно в теории и без ndk сделать

T8RIN commented 10 months ago

С шейдерами то знаком, но ток с тем как их использовать готовые, сам писать не дорос ))

awxkee commented 10 months ago

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

awxkee commented 10 months ago

И есть опция opencv, там нету всех моих опций, зато есть куча готовых либ на андроид, и все встроиться минут за 15, ценой плюс 15-20мб к каждому бинарнику