-1

Switching to native to get more performance and processing speed, but unfortunately, my app is too slow. Also, when loading high-resolution images, the app crashes.

Here is my full code for you to tell me how to improve it.

java code:

package com.example.invert;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {
    ImageView imageView2;
    double[][][] imgArray;
    int w, h;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView2 = (ImageView) findViewById(R.id.imageView1);
        imageView2.setDrawingCacheEnabled(true);
        BitmapDrawable bitmapDrawable = (BitmapDrawable) imageView2
                .getDrawable();
        final Bitmap bitmap = bitmapDrawable.getBitmap();
        Button button = (Button) findViewById(R.id.button1);

        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                w = bitmap.getWidth();
                h = bitmap.getHeight();
                imgArray = new double[w][h][3];

                for (int i = 0; i < w; i++) {
                    for (int j = 0; j < h; j++) {
                        imgArray[i][j][0] = Color.red(bitmap.getPixel(i, j));
                        imgArray[i][j][1] = Color.green(bitmap.getPixel(i, j));
                        imgArray[i][j][2] = Color.blue(bitmap.getPixel(i, j));

                    }
                }

                imgArray = inv(imgArray, w, h);
                Bitmap newBitmap = Bitmap.createBitmap(w, h, bitmap.getConfig());
                for (int i = 0; i < w; i++) {
                    for (int j = 0; j < h; j++) {
                        newBitmap.setPixel(i, j, Color.rgb(
                                (int) (imgArray[i][j][0]),
                                (int) (imgArray[i][j][1]),
                                (int) (imgArray[i][j][2])));
                    }
                }

                imageView2.setImageBitmap(newBitmap);

            }
        });

    }

    static {
        System.loadLibrary("inv");
    }

    // internal, private
    public native double[][][] inv(double[][][] inputArr, int w, int h);

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

C code:

#include <jni.h>
#include <stdio.h>
#include<stddef.h>


JNIEXPORT jobjectArray JNICALL Java_com_example_invert_MainActivity_inv
  (JNIEnv *env, jobject obj, jobjectArray arr, jint w, jint h){
    double sum = 0;
    int i,j,k;
    double a[w][h][3];
     jsize dim1 = (*env)->GetArrayLength(env, arr);

       for (i=0; i<w; i++){

            jdoubleArray *line1 =   (*env)->GetObjectArrayElement(env, arr, i);
            int dim2 =       (*env)->GetArrayLength(env, line1);
            jdouble *pos1 = (*env)->GetDoubleArrayElements(env, line1, 0);

            for (j=0; j<h; j++){
                jdoubleArray *line2 =   (*env)->GetObjectArrayElement(env, line1, j);
                int dim3 =       (*env)->GetArrayLength(env, line2);
                jdouble *pos2 = (*env)->GetDoubleArrayElements(env, line2, 0);

                for (k=0; k<dim3; k++){
                        a[i][j][k]= pos2[k];
                    }
                (*env)->ReleaseDoubleArrayElements(env, line2, pos2, 0);
                (*env)->DeleteLocalRef(env, line2);
              }

            (*env)->ReleaseDoubleArrayElements(env, line1, pos1, 0);
            (*env)->DeleteLocalRef(env, line1);
       }




        jclass doubleArrayArrayClass = (*env)->FindClass(env,"[[D");
        jclass doubleArrayClass = (*env)->FindClass(env,"[D");


        jobjectArray ret  = (*env)->NewObjectArray(env,w, doubleArrayArrayClass, NULL);

        for( i = 0; i<w; i++){
            for( j = 0; j<h; j++){
                for( k = 0; k<3; k++){

                    a[i][j][k] = 255 - a[i][j][k];
                }
            }
        }

        for( i = 0; i<w; i++){

            jobjectArray dim2 = (*env)->NewObjectArray(env, w, doubleArrayClass, NULL);
            for( j = 0; j<h; j++) {

                jdoubleArray dim1 = (*env)->NewDoubleArray(env,h);
                jdouble tmp[3];
                for( k = 0; k<3; k++){
                    tmp[k] = a[i][j][k];
                }
                (*env)->SetDoubleArrayRegion(env,dim1 , 0, h, tmp);
                (*env)->SetObjectArrayElement(env, dim2, j, dim1);
                (*env)->DeleteLocalRef(env, dim1);
            }
            (*env)->SetObjectArrayElement(env,ret, i, dim2);
            (*env)->DeleteLocalRef(env,dim2);
        }
        return ret;
}
sKhan
  • 9,694
  • 16
  • 55
  • 53
  • You should be more specific about what happens when the app 'crashes'. Do you get any traceback or error messages. As to being slow, you should learn how to profile your code to find out where it is taking the most time – joel goldstick Feb 20 '16 at 23:24
  • [*Try this.*](http://stackoverflow.com/a/378024/23771) It's not a matter of finding out *where* it is taking the most time, but *why*. – Mike Dunlavey Feb 21 '16 at 17:16

1 Answers1

2

access to 3-dimensional array is much slower both in Java and JNI than access to 1-dimensional array of same size. Therefore I would strongly recommend to create in Java imgArray = new double[w*h*3] and work with it.

Same applies to the output array.

Also, using SetDoubleArrayRegion(), you introduce an extra memcopy; better, use double* cArray = GetDoubleArrayElements(env, jArray, 0), put the values directly into cArray, and release it to Java with ReleaseDoubleArrayElements(env, jArray, cArray, 0). This 0 means that the changes to cArray will be seen in jArray on the Java side.

Also, Android NDK provides direct access to Bitmap pixels from C via #include "android/bitmap.h". Working with getPixel() and setPixel() adds huge overhead.

On the C side, most likely, your crash happens because for a big bitmap allocation of a[w][h][3] on stack fails. Stack is not intended to hold huge data chunks. You must allocate your array in the heap.

Your code will probably be much cleaner if you switch to C++ and use std::vector() and other helpful shortcuts.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • I wrote these tips before; did you try them, and found that they don't give you the expected boost? Or you did not understand how to follow them, and need more guidance? – Alex Cohn Feb 21 '16 at 10:49
  • 1-for the first tip, I would prefer to keep the [ ][ ][ ] pattern because, I'm gonna implement an algo requiring many 3D loops... 2- I didn't get your point: can you explane more? 3- For bitmap.h, I don't know how to extract pixels. can it be used exactly as the one of android? and is it better that passing the image as array? thanks btw :) – user5956131 Feb 21 '16 at 13:32
  • **1.** Access to "planar" 3D array is much more efficient than array of arrays of arrays in any language. C language has a deceptive feature that you use in your code: you can allocate `double a[w][h][3]` on stack as an efficient contiguous array, or use `double*** b` and allocate a 3-dimensional non-contiguous structure in the heap. Access to **a[x][y][c]** is much more efficient than **b[x][y][c]**, but stack is a very limited resource. Most likely, your crash happens because for a big bitmap this allocation fails. – Alex Cohn Feb 21 '16 at 16:28
  • **2.** Apart from access to array memory, covered above, there is another tradeoff: you want to minimize amount of copying pixel memory. The best would be to load a bitmap, lock its pixels (i.e. get a pointer to the first pixel), and work with them in-place, without copying the data. `SetDoubleArrayRegion()` does not work in-place, `GetDoubleArrayElements()` can, and on Android (non-contractual behavior) it does work without copy. – Alex Cohn Feb 21 '16 at 16:32
  • **3.** But if your source is an Android bitmap, direct access is much much faster and it was designed specifically for scenarios like yours. [AndroidBitmap_lockPixels()](http://developer.android.com/ndk/reference/group___bitmap.html#ga2908d42fa4db286c34b7f8c11f29206f) allows direct access to the pixles. You can find many examples, e.g. http://ruckus.tumblr.com/post/18055652108/writing-a-basic-image-filter-in-android-using-ndk – Alex Cohn Feb 21 '16 at 16:37
  • I found a tut about bitmap.h. Yeah you're right, it's much faster!. I want to use it to implement the invert effect. but my code doesn't work. here is the question:[link](http://stackoverflow.com/questions/35590212/jni-image-processing-with-bitmap-h) – user5956131 Feb 23 '16 at 23:21