OpenCV OpenCL: Convert Mat to Bitmap in JNI Layer for Android - android

There are several posts about converting Mat to Bitmap using the Utils.matToBitmap() function. But I'm assuming this function can only be called in the Java layer after importing the Utils class.
I want to transfer the data to a memory address pointed to by uint32_t* bmpContent; in the code below.
JNIEXPORT void JNICALL Java_com_nod_nodcv_NodCVActivity_runfilter(
JNIEnv *env, jclass clazz, jobject outBmp, jbyteArray inData,
jint width, jint height, jint choice, jint filter)
{
int outsz = width*height;
int insz = outsz + outsz/2;
AndroidBitmapInfo bmpInfo;
if (AndroidBitmap_getInfo(env, outBmp, &bmpInfo) < 0) {
throwJavaException(env,"gaussianBlur","Error retrieving bitmap meta data");
return;
}
if (bmpInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
throwJavaException(env,"gaussianBlur","Expecting RGBA_8888 format");
return;
}
uint32_t* bmpContent;
if (AndroidBitmap_lockPixels(env, outBmp,(void**)&bmpContent) < 0) {
throwJavaException(env,"gaussianBlur","Unable to lock bitmap pixels");
return;
}
//This function runs the kernel on the inData and gives a matrix
tester(env, clazz, bmpContent, outsz, inData, insz, width, height);
AndroidBitmap_unlockPixels(env, outBmp);
}
This is roughly what happens in the tester function:
jbyte* b_mat = env->GetByteArrayElements(inData, 0);
cv::Mat mdata(h, w, CV_8UC4, (unsigned char *)b_mat);
cv::Mat mat_src = imdecode(mdata,1);
cv::UMat umat_src = mat_src.getUMat(cv::ACCESS_READ, cv::USAGE_ALLOCATE_DEVICE_MEMORY);
cv::UMat umat_dst (mat_src.size(), mat_src.type(), cv::ACCESS_WRITE, cv::USAGE_ALLOCATE_DEVICE_MEMORY);
kernel.args(cv::ocl::KernelArg::ReadOnlyNoSize(umat_src), cv::ocl::KernelArg::ReadWrite(umat_dst));
size_t globalThreads[3] = {static_cast<unsigned int>(mat_src.cols), static_cast<unsigned int>(mat_src.rows), 1 };
bool success = kernel.run(3, globalThreads, NULL, true);
cv::Mat mat_dst = umat_dst.getMat(cv::ACCESS_READ);
mat_dst holds the results I need and that I need to display on my phone.
How can I do that?
I'm assuming I'll need to copy the data from mat_dst to the bmpContent place, but I'm not sure.

If you really need to call this method from the JNI layer, you could simply use the OpenCV's original C++ implementation Here.
An example code would be like:
#include <jni.h>
#include <string>
#include <android/bitmap.h>
#include "opencv2/opencv.hpp"
// using namespace cv;
void MatToBitmap2 (JNIEnv * env, cv::Mat src, jobject bitmap, bool needPremultiplyAlpha)
{
AndroidBitmapInfo info;
void* pixels = 0;
try {
// LOGD("nMatToBitmap");
CV_Assert( AndroidBitmap_getInfo(env, bitmap, &info) >= 0 );
CV_Assert( info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
info.format == ANDROID_BITMAP_FORMAT_RGB_565 );
CV_Assert( src.dims == 2 && info.height == (uint32_t)src.rows && info.width == (uint32_t)src.cols );
CV_Assert( src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4 );
CV_Assert( AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0 );
CV_Assert( pixels );
if( info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 )
{
cv::Mat tmp(info.height, info.width, CV_8UC4, pixels);
if(src.type() == CV_8UC1)
{
cvtColor(src, tmp, cv::COLOR_GRAY2RGBA);
} else if(src.type() == CV_8UC3){
cvtColor(src, tmp, cv::COLOR_RGB2RGBA);
} else if(src.type() == CV_8UC4){
if(needPremultiplyAlpha) cvtColor(src, tmp, cv::COLOR_RGBA2mRGBA);
else src.copyTo(tmp);
}
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
if(src.type() == CV_8UC1)
{
cvtColor(src, tmp, cv::COLOR_GRAY2BGR565);
} else if(src.type() == CV_8UC3){
cvtColor(src, tmp, cv::COLOR_RGB2BGR565);
} else if(src.type() == CV_8UC4){
cvtColor(src, tmp, cv::COLOR_RGBA2BGR565);
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return;
} catch(const cv::Exception& e) {
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
return;
} catch (...) {
AndroidBitmap_unlockPixels(env, bitmap);
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
return;
}
}
The function was directly adopted from the OpenCV sources and contains some extra checks for different formats. You could just strip the checks out if you knew what matrix format you're going to use.
mat_dst holds the results I need and that I need to display on my phone. How can I do that?
You can call something like:
extern "C"
JNIEXPORT void JNICALL
Java_com_your_package_MainActivity_DoStuff(JNIEnv *env, jobject thiz,
jobject bitmap) {
// Do your stuff with mat_dst.
try {
MatToBitmap2(env, mat_dst, bitmap, false);
}
catch(const cv::Exception& e)
{
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, e.what());
}
}
and define it in the Java side like:
public native void DoStuff(Bitmap bitmap);
You don't need to return anything back to java side as the Bitmap class would be reference type and the MatToBitmap2 method would already take care of locking and unlocking pixels buffer.

Use this to convert your Mat to Bitmap.
jclass java_bitmap_class = (jclass)env->FindClass("android/graphics/Bitmap");
jmethodID mid = env->GetMethodID(java_bitmap_class, "getConfig", "()Landroid/graphics/Bitmap$Config;");
jobject bitmap_config = env->CallObjectMethod(bitmap, mid);
jobject _bitmap = mat_to_bitmap(env,dst,false,bitmap_config);
AndroidBitmap_unlockPixels(env, bitmap);
return _bitmap;

Related

Deserializing shape_predictor_68_face_landmarks.dat only for once for facial landmark detection in real time in android studio

I am trying to detect facial landmark using opencv ,dlib library in android studio.
I can capture image and detect landmarks from the image.
But I am facing problem when I am trying to detect facial landmark in real time.
Because I am deserializing shape_predictor_68_face_landmarks.dat file for each frame.
onCameraFrame method of MainActivity.Java
#Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame)
{
matInput =inputFrame.rgba();
NativeClass.LandmarkDetection(matInput.getNativeObjAddr(), matOutput.getNativeObjAddr());
return matOutput;
}
NativeClass.Java
package com.example.user.drowsinessdetection;
public class NativeClass {
public native static String getMessage();
public native static void LandmarkDetection(long addrInput,long addrOutput);
}
The cpp file for Landmark detection:
com_example_user_drowsinessdetection_NativeClass.cpp
#include <com_example_user_drowsinessdetection_NativeClass.h>
#include <dlib/geometry/rectangle.h>
JNIEXPORT jstring JNICALL
Java_com_example_user_drowsinessdetection_NativeClass_getMessage
(JNIEnv *env, jclass){
env->NewStringUTF("JNI message");
}
JNIEXPORT void JNICALL
Java_com_example_user_drowsinessdetection_NativeClass_LandmarkDetection
(JNIEnv *env, jclass thiz, jlong addrInput, jlong addrOutput){
Mat& image = *(Mat*)addrInput;
Mat& dst = *(Mat*)addrOutput;
faceDetectionDlib(image, dst);
}
void faceDetectionDlib(Mat& img, Mat& dst){
try {
frontal_face_detector detector = get_frontal_face_detector();
shape_predictor pose_model;
deserialize("storage/emulated/0/shape_predictor_68_face_landmarks.dat")>>pose_model;
cv_image<bgr_pixel>cimg(img);
std::vector<dlib::rectangle> faces = detector(cimg);
std::vector<full_object_detection> shapes;
int k=faces.size();
for(unsigned long i = 0;i < k; ++i)
shapes.push_back(pose_model(cimg,faces[i]));
dst = img.clone();
renderToMat(shapes, dst);
}
catch (serialization_error& e)
{
cout<<endl<<e.what()<<endl;
}
}
void renderToMat(std::vector<full_object_detection>& dets, Mat& dst){
Scalar color;
int sz = 3,l;
color = Scalar(0,255,0);
//chin line
l=dets.size();
for(unsigned long idx = 0; idx < l; idx++) {
//left eye
for (unsigned long i = 37; i <= 41; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()),
Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(36).x(), dets[idx].part(36).y()),
Point(dets[idx].part(41).x(), dets[idx].part(41).y()), color, sz);
//right eye
for (unsigned long i = 43; i <= 47; ++i)
cv::line(dst, Point(dets[idx].part(i).x(), dets[idx].part(i).y()),
Point(dets[idx].part(i - 1).x(), dets[idx].part(i - 1).y()), color, sz);
cv::line(dst, Point(dets[idx].part(42).x(), dets[idx].part(42).y()),
Point(dets[idx].part(47).x(), dets[idx].part(47).y()), color, sz);
//lips out part
}
}
I don't want execute this line
frontal_face_detector detector = get_frontal_face_detector();
and
deserialize("storage/emulated/0/shape_predictor_68_face_landmarks.dat")>>pose_model;
for each and every frame.
So How can I do these only once and then use these for all other frames?
I am a newbie.
Please help.
Sorry for my poor English. :)

native activity camera on lollipop with opencv

It seems that opencv can't use native camera on Android 5.+ ( lollipop ).
cf :
http://code.opencv.org/issues/4185
Is there an other way to grab pictures from a native activity and then convert into cv::mat ?
Or, maybe I could use jni to call a grab function in java from my c++ activity ?
Thank you for your help
Charles
You could use jni to call a grab function in java from c++ activity, like this(threshold example):
Java code:
//Override JavaCameraView OpenCV function
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
ProcessImage.threshold(mGray, mGray, 97, 0);
return mGray;
}
// Your functions
public static void threshold(Mat srcGray, Mat dst, int thresholdValue, int thresholdType) {
nativeThreshold(srcGray.getNativeObjAddr(), dst.getNativeObjAddr(), thresholdValue, thresholdType.ordinal());
}
private static native void nativeThreshold(long srcGray, long dst, int thresholdValue, int thresholdType);
JNI c++ code:
JNIEXPORT void JNICALL Java_{package}_nativeThreshold
(JNIEnv * jenv, jobject jobj, jlong scrGray, jlong dst, jint thresholdValue, jint thresholdType)
{
try
{
Mat matDst = *((Mat*)dst);
Mat matSrcGray = *((Mat*)scrGray);
threshold( matSrcGray, matDst, thresholdValue, max_BINARY_value, thresholdType );
}
catch(cv::Exception& e)
{
LOGD("nativeThreshold caught cv::Exception: %s", e.what());
jclass je = jenv->FindClass("org/opencv/core/CvException");
if(!je)
je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, e.what());
}
catch (...)
{
LOGD("nativeThreshold caught unknown exception");
jclass je = jenv->FindClass("java/lang/Exception");
jenv->ThrowNew(je, "Unknown exception in JNI code ProcessImage.nativeThreshold()");
}
}
Hope this helps!

Android NDK JNI array reference table overflow

I have two functions and I'm getting a ReferenceTable overflow.
The summary of consumed array entries is:
1 of byte[] (3 elements)
446 of byte[] (75 elements) (2 unique instances)
576 of byte[] (147 elements) (2 unique instances)
1 of int[] (25 elements)
I really checked the code to find any mistake, but didn't found it. Im releasing arrays after getting them. The only thing is that these functions are called thousands of times, can this be the cause?
Here is all my native code:
called once:
JNIEXPORT void JNICALL Java_ar_com_teasoft_Image_nativeUnlock(
JNIEnv *env, jclass clazz, jobject bitmap) {
AndroidBitmap_unlockPixels(env, bitmap);
}
called once:
JNIEXPORT jlong JNICALL Java_ar_com_teasoft_Image_nativeLock(
JNIEnv *env, jclass clazz, jobject bitmap) {
int ret;
AndroidBitmapInfo info;
if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return 0;
}
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
LOGE("Bitmap format is not RGBA_8888!");
return 0;
}
void* bitmapPixels;
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0) {
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return 0;
}
return (jlong) bitmapPixels;
}
called lots of times:
JNIEXPORT void JNICALL Java_ar_com_teasoft_Image_nativeCopyPixels(
JNIEnv *env, jclass clazz, jlong dataRef, jintArray sourceIndexes,
jintArray targetIndexes, jint count) {
argb* sourcePixels;
argb* targetPixels;
jint *sourceArray = env->GetIntArrayElements(sourceIndexes, NULL);
jint *targetArray = env->GetIntArrayElements(targetIndexes, NULL);
for (int i = 0; i < count; i++) {
sourcePixels = (argb*)((char*) dataRef + sourceArray[i] * 4);
targetPixels = (argb*)((char*) dataRef + targetArray[i] * 4);
(*targetPixels) = (*sourcePixels);
}
env->ReleaseIntArrayElements(sourceIndexes, sourceArray, JNI_ABORT);
env->ReleaseIntArrayElements(targetIndexes, targetArray, JNI_ABORT);
}
called lots of times:
JNIEXPORT void JNICALL Java_ar_com_teasoft_Image_nativeGetRGB(
JNIEnv *env, jclass clazz, jlong dataRef, jintArray indexes,
jbyteArray destRgb) {
jint *array = env->GetIntArrayElements(indexes, NULL);
jbyte *dstarray = env->GetByteArrayElements(destRgb, NULL);
int size = env->GetArrayLength(indexes);
char* sourcePixels;
int dstCount = 0;
for (int i = 0; i < size; i++) {
sourcePixels = (char*) dataRef + array[i] * 4;
dstarray[dstCount++] = (*(sourcePixels + 1));
dstarray[dstCount++] = (*(sourcePixels + 2));
dstarray[dstCount++] = (*(sourcePixels + 3));
}
env->ReleaseIntArrayElements(indexes, array, JNI_ABORT);
env->ReleaseByteArrayElements(destRgb, dstarray, JNI_COMMIT);
}
Based on the summary, it looks like the one that is not released, is of byte[], so it has to be the one in function nativeGetRGB. But i cannot find where the mistake is.
Please Help!
Regards,
Juan Ignacio
Java_ar_com_teasoft_Image_nativeGetRGB():
As far as I can see, you'd need to commit and free any temporary array copy by passing 0 instead of JNI_COMMIT to ReleaseByteArrayElements(). The second argument of Get*ArrayElements() is a pointer to a boolean, which will be set to true, if the returned array is a copy, instead of pinned memory.
Java_ar_com_teasoft_Image_nativeCopyPixels():
You might also want to pass 0 instead of JNI_ABORT, which discards everything, here:
env->ReleaseIntArrayElements(targetIndexes, targetArray, JNI_ABORT);
The tricky thing with arrays is, that the release mode applies to copied arrays only, since pinned memory gets modified directly. There's no way to force either array copy or pinning.

How to use JNI bitmap operations for helping to avoid OOM when using large images? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Background
most of the times, getting OOM on android is due to using too many bitmaps and/or creating large bitmaps.
recently i've decided to try out JNI in order to allow avoiding OOM by storing the data itself on the JNI side.
after messing around with JNI for a while, i've created some posts on SO asking for help and sharing my knowledge, and i've now decided to share some more code with you. here are the posts in case anyone is interested in reading the findings or contributing :
How to cache bitmaps into native memory
image decoding and manipulation using JNI on android
JNI - how to use multiple Jni wrapper instances with different fields?
Rotating a bitmap using JNI & NDK
this time, i've added the ability to store,restore, crop and rotate bitmaps. it should be easy to add more options and I would be happy if other people here would add their own code to more useful functions .
so the code i'm about to show is actually merging of all the things i've created.
Sample code of usage:
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
final int width=bitmap.getWidth(),height=bitmap.getHeight();
// store the bitmap in the JNI "world"
final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
// no need for the bitmap on the java "world", since the operations are done on the JNI "world"
bitmap.recycle();
// crop a center square from the bitmap, from (0.25,0.25) to (0.75,0.75) of the bitmap.
bitmapHolder.cropBitmap(width/4,height/4,width*3/4,height*3/4);
//rotate the bitmap:
bitmapHolder.rotateBitmapCcw90();
//get the output java bitmap , and free the one on the JNI "world"
bitmap=bitmapHolder.getBitmapAndFree();
The project is available on github
project page is available on github here .
feel free to give advises and contribute.
Important notes
same notes as shown here, plus:
current features that are written here (more updated on the project page) :
store
restore
rotate 90 degrees CCW
crop.
the approach i've taken for this code is both memory efficiency (use only memory that i need, and free it when not needed), and CPU efficiency (i tried to use pointers and CPU memory cache optimizations whenever possible).
for best performance, i've done really few validations, especially on the JNI part. it might be best to manage the validations on the java "world".
there are still many missing features that i think should be added, and i hope that i will have the time to add them . if anyone wishes to contribute, i will be glad to add they code too. here are the functions that i think could be useful:
get current bitmap info
scale bitmaps, including choice of which algorithm to use (nearest neighbour and bilinear interpolation should be enough).
use different bitmap formats
do the decoding within JNI, to avoid creation of the java bitmap (and not use the heap on the java world) from the beginning, only at the end, when you finished with all of the operations.
face detection
rotation in any angle, or at least the obvious ones . currently i only added rotation of 90 degrees counter clock wise .
NOTE: this is a bit old code. for the most updated one, check out the project page on github.
jni/Android.mk
LOCAL_PATH := $(call my-dir)
#bitmap operations module
include $(CLEAR_VARS)
LOCAL_MODULE := JniBitmapOperations
LOCAL_SRC_FILES := JniBitmapOperations.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics
include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g
#if you need to add more module, do the same as the one we started with (the one with the CLEAR_VARS)
jni/JniBitmapOperations.cpp
#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>
#define LOG_TAG "DEBUG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C"
{
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle);
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom);
}
class JniBitmap
{
public:
uint32_t* _storedBitmapPixels;
AndroidBitmapInfo _bitmapInfo;
JniBitmap()
{
_storedBitmapPixels = NULL;
}
};
/**crops the bitmap within to be smaller. note that no validations are done*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniCropBitmap(JNIEnv * env, jobject obj, jobject handle, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
uint32_t* previousData = jniBitmap->_storedBitmapPixels;
uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
uint32_t newWidth = right - left, newHeight = bottom - top;
uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
uint32_t* whereToGet = previousData + left + top * oldWidth;
uint32_t* whereToPut = newBitmapPixels;
for (int y = top; y < bottom; ++y)
{
memcpy(whereToPut, whereToGet, sizeof(uint32_t) * newWidth);
whereToGet += oldWidth;
whereToPut += newWidth;
}
//done copying , so replace old data with new one
delete[] previousData;
jniBitmap->_storedBitmapPixels = newBitmapPixels;
jniBitmap->_bitmapInfo.width = newWidth;
jniBitmap->_bitmapInfo.height = newHeight;
}
/**rotates the inner bitmap data by 90 degress counter clock wise*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniRotateBitmapCcw90(JNIEnv * env, jobject obj, jobject handle)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
uint32_t* previousData = jniBitmap->_storedBitmapPixels;
AndroidBitmapInfo bitmapInfo = jniBitmap->_bitmapInfo;
uint32_t* newBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
int whereToPut = 0;
// A.D D.C
// ...>...
// B.C A.B
for (int x = bitmapInfo.width - 1; x >= 0; --x)
for (int y = 0; y < bitmapInfo.height; ++y)
{
uint32_t pixel = previousData[bitmapInfo.width * y + x];
newBitmapPixels[whereToPut++] = pixel;
}
delete[] previousData;
jniBitmap->_storedBitmapPixels = newBitmapPixels;
uint32_t temp = bitmapInfo.width;
bitmapInfo.width = bitmapInfo.height;
bitmapInfo.height = temp;
}
/**free bitmap*/ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
delete[] jniBitmap->_storedBitmapPixels;
jniBitmap->_storedBitmapPixels = NULL;
delete jniBitmap;
}
/**restore java bitmap (from JNI data)*/ //
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
{
LOGD("no bitmap data was stored. returning null...");
return NULL;
}
//
//creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
//
//LOGD("creating new bitmap...");
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jstring configName = env->NewStringUTF("ARGB_8888");
jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, jniBitmap->_bitmapInfo.width, jniBitmap->_bitmapInfo.height, bitmapConfig);
//
// putting the pixels into the new bitmap:
//
int ret;
void* bitmapPixels;
if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
{
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return NULL;
}
uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
int pixelsCount = jniBitmap->_bitmapInfo.height * jniBitmap->_bitmapInfo.width;
memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels, sizeof(uint32_t) * pixelsCount);
AndroidBitmap_unlockPixels(env, newBitmap);
//LOGD("returning the new bitmap");
return newBitmap;
}
/**store java bitmap as JNI data*/ //
JNIEXPORT jobject JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap)
{
AndroidBitmapInfo bitmapInfo;
uint32_t* storedBitmapPixels = NULL;
//LOGD("reading bitmap info...");
int ret;
if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0)
{
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return NULL;
}
LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);
if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
{
LOGE("Bitmap format is not RGBA_8888!");
return NULL;
}
//
//read pixels of bitmap into native memory :
//
//LOGD("reading bitmap pixels...");
void* bitmapPixels;
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0)
{
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return NULL;
}
uint32_t* src = (uint32_t*) bitmapPixels;
storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
int pixelsCount = bitmapInfo.height * bitmapInfo.width;
memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelsCount);
AndroidBitmap_unlockPixels(env, bitmap);
JniBitmap *jniBitmap = new JniBitmap();
jniBitmap->_bitmapInfo = bitmapInfo;
jniBitmap->_storedBitmapPixels = storedBitmapPixels;
return env->NewDirectByteBuffer(jniBitmap, 0);
}
src/com/jni/bitmap_operations/JniBitmapHolder.java
package com.jni.bitmap_operations;
import java.nio.ByteBuffer;
import android.graphics.Bitmap;
import android.util.Log;
public class JniBitmapHolder
{
ByteBuffer _handler =null;
static
{
System.loadLibrary("JniBitmapOperations");
}
private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);
private native Bitmap jniGetBitmapFromStoredBitmapData(ByteBuffer handler);
private native void jniFreeBitmapData(ByteBuffer handler);
private native void jniRotateBitmapCcw90(ByteBuffer handler);
private native void jniCropBitmap(ByteBuffer handler,final int left,final int top,final int right,final int bottom);
public JniBitmapHolder()
{}
public JniBitmapHolder(final Bitmap bitmap)
{
storeBitmap(bitmap);
}
public void storeBitmap(final Bitmap bitmap)
{
if(_handler!=null)
freeBitmap();
_handler=jniStoreBitmapData(bitmap);
}
public void rotateBitmapCcw90()
{
if(_handler==null)
return;
jniRotateBitmapCcw90(_handler);
}
public void cropBitmap(final int left,final int top,final int right,final int bottom)
{
if(_handler==null)
return;
jniCropBitmap(_handler,left,top,right,bottom);
}
public Bitmap getBitmap()
{
if(_handler==null)
return null;
return jniGetBitmapFromStoredBitmapData(_handler);
}
public Bitmap getBitmapAndFree()
{
final Bitmap bitmap=getBitmap();
freeBitmap();
return bitmap;
}
public void freeBitmap()
{
if(_handler==null)
return;
jniFreeBitmapData(_handler);
_handler=null;
}
#Override
protected void finalize() throws Throwable
{
super.finalize();
if(_handler==null)
return;
Log.w("DEBUG","JNI bitmap wasn't freed nicely.please rememeber to free the bitmap as soon as you can");
freeBitmap();
}
}

Android ndk out of memory issue

i am trying to do real time image processing in android using jni. I have a native method to decode image data and i call this method for every frame. After a few seconds later i get out of memory and my app terminats.
LOG OUTPUT:
12-03 20:54:19.780: E/dalvikvm-heap(8119): Out of memory on a 3686416-byte allocation.
MY NATIVE METHOD:
JNIEXPORT jintArray JNICALL Java_net_oyunyazar_arcc_data_FrameManager_processImage(JNIEnv* env, jobject javaThis, jint width, jint height, jbyteArray arr) {
jint *convertedData;
convertedData = (jint*)malloc((width*height) * sizeof(jint));
jintArray result = (*env)->NewIntArray(env, width*height);
jint y,x;
jbyte grey;
jsize len = (*env)->GetArrayLength(env, arr);
jbyte *YUVData = (*env)->GetByteArrayElements(env, arr, 0);
for (y = 0; y < height; y++){
for (x = 0; x < width; x++){
grey = YUVData[y * width + x];
convertedData[y*width+x] =(jint) grey & 0xff;
}
}
LOGD("Random [%d]",len);
(*env)->SetIntArrayRegion(env, result, 0, (width*height),convertedData );
free(convertedData);
(*env)->ReleaseByteArrayElements(env, YUVData, (jbyte*)arr, 0);
return result;
}
Thanks for any help.
I have the same problem as yours.
In your specific case, while you are using pixel (and probably bitmap) you can send a bitmap instead of your bytearray and modify it
void *pixel_bm;
int retValue;
AndroidBitmapInfo info;
if ((retValue = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) return 0;
if ((retValue = AndroidBitmap_lockPixels(env, bitmap, &pixel_bm)) < 0) return 0;
// you can now read an write into pixel_bm
AndroidBitmap_unlockPixels(env, bitmap);
If you find a solution to correctly free a GetByteArrayElement result, I'm instrested by the solution !!!
I have solved this problem by releasing the parameters.
(*env)->ReleaseByteArrayElements(env, arr, YUVData, 0);
It works great now.

Categories

Resources