Декодирование изображений и манипуляции с использованием JNI на Android

фон

В некоторых приложениях важно обрабатывать большие изображения без OOM, а также быстро.

Для этого JNI (или renderscript, который, к сожалению, отсутствует в документации), может быть хорошим решением.

В прошлом мне удалось использовать JNI для вращения огромных растровых изображений, избегая при этом OOM (ссылка здесь, здесь и здесь). это был приятный (но досадно тяжелый) опыт, но в итоге он сработал.

проблема

В андроидном фреймворке есть много функций для обработки растровых изображений, но я не знаю, какова ситуация на стороне JNI.

Я уже знаю, как передать растровое изображение из андроида "java world" в "мир JNI" и обратно.

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

Я хочу иметь возможность выполнять все операции с изображениями (включая декодирование) в JNI, поэтому мне не нужно беспокоиться о OOM при представлении больших изображений, и в конце процесса я мог бы преобразовать данные на Java-bitmap (чтобы показать пользователя) и/или записать его в файл.

снова, я не хочу преобразовывать данные на стороне JNI в растровое изображение java, чтобы иметь возможность запускать эти операции.

Как оказалось, есть несколько библиотек, которые предлагают множество функций (например, JavaCV), но они достаточно велики, и я не совсем уверен в их возможностях, и если они действительно делают декодирование на стороне JNI, поэтому я предпочел бы знать, что возможно, используя встроенную функцию JNI для Android.

вопрос

какие функции доступны для обработки изображений на стороне JNI на андроиде?

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

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

даже если я сам реализую свои действия, возможно, другие сделали это намного эффективнее, думая о том, сколько оптимизаций может иметь C/С++.

Я действительно сам по себе, когда отправляюсь на сторону JNI android, где мне нужно реализовать все с нуля?

просто, чтобы понять, что меня интересует:

входной битмап на java → манипуляция изображениями исключительно в JNI и C/С++ (без преобразования в java-объекты) → вывод растрового изображения на java.

Ответ 1

"встроенная функция JNI для Android" - это вид оксюморона. Это технически правильно, что многие классы Java Framework Java используют JNI где-то в цепочке, чтобы вызвать родные библиотеки.

Но есть три оговорки относительно этого утверждения.

  • Это "детали реализации" и могут быть изменены без уведомления в любой следующей версии Android или любой вилки (например, Kindle) или даже версии OEM, которая не считается "fork" (например, построено компанией Samsung или для Quallcom SOC).

  • Способ, которым встроенные методы реализуются в основных классах Java, отличается от "классического" JNI. Эти методы предварительно загружаются и кэшируются JVM и поэтому не страдают от большей части служебных данных, типичных для вызовов JNI.

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

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


Чтобы дать конкретный пример, обнаружение лица в Android реализовано с помощью класса android.media.FaceDetector, который загружает libFFTEm.so. Вы можете посмотреть собственный код и использовать его по своему усмотрению. Вы не должны предполагать, что libFFTEm.so будет присутствовать на устройстве или что библиотека на устройстве будет иметь тот же API.

Но в этом конкретном случае это не проблема, потому что вся работа neven полностью основана на программном обеспечении. Поэтому вы можете скопировать этот код целиком или только соответствующие его части и сделать его частью вашей родной библиотеки. Обратите внимание, что для многих устройств вы можете просто загружать и использовать /system/lib/libFFTEm.so и никогда не чувствовать дискомфорта, пока не столкнетесь с системой, которая будет плохо себя вести.

Один замечательный вывод, который вы можете сделать, прочитав собственный код, заключается в том, что основные алгоритмы игнорируют информацию о цвете. Поэтому, если изображение, для которого вы хотите найти координаты лица, поступает из источника YUV, вы можете избежать множества накладных расходов, если вы вызываете

// run detection
btk_DCR_assignGrayByteImage(hdcr, bwbuffer, width, height);

int numberOfFaces = 0;
if (btk_FaceFinder_putDCR(hfd, hdcr) == btk_STATUS_OK) {
    numberOfFaces = btk_FaceFinder_faces(hfd);
} else {
    ALOGE("ERROR: Return 0 faces because error exists in btk_FaceFinder_putDCR.\n");
}

непосредственно с вашим массивом байтов YUV (или Y) вместо преобразования его в RGB и обратно в YUV в android.media.FaceDetector.findFaces(). Если ваш буфер YUV поступает из Java, вы можете создать собственный класс YuvFaceDetector, который будет копией android.media.FaceDetector с помощью только разница, что YuvFaceDetector.findFaces() будет принимать значения Y (яркости) только вместо растрового изображения и избегать преобразования RGB в Y.


Некоторые другие ситуации не так просты. Например, видеокодеки тесно связаны с аппаратной платформой, и вы не можете просто скопировать код из libstagefright.so в свой проект. Jpeg-кодек - специальный зверь. В современных системах (IIRC, начиная с версии 2.2) вы можете ожидать, что /system/lib/libjpeg.so будет присутствовать. Но многие платформы также имеют намного более эффективные HW-реализации Jpeg-кодеков через libstagefright.so или OpenMAX, и часто они используются в android.graphics.Bitmap.compress( ) и android.graphics.BitmapFactory.decode ***() методы.

И также есть оптимизированный libjpeg-turbo, который имеет свои преимущества перед /system/lib/libjpeg.so.

Ответ 2

Кажется, что ваш вопрос связан скорее с библиотеками обработки изображений C/С++, чем с Android. С этой целью, вот некоторые другие вопросы StackOverflow, которые могут иметь полезную информацию:

Библиотеки обработки изображений с быстрой кросс-платформенной C/С++

Библиотеки обработки изображений С++