jni - 本机处理缓慢

jni - Slow processing in native

切换到本机以获得更高的性能和处理速度,但不幸的是,我的应用程序太慢了。此外,在加载高分辨率图像时,应用程序会崩溃。

这是我的完整代码,您可以告诉我如何改进它。

java代码:

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代码:

#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;
}

在 Java 和 JNI 中访问 3 维数组比访问相同大小的 1 维数组慢得多。因此,我强烈建议在 Java imgArray = new double[w*h*3] 中创建并使用它。

同样适用于输出数组。

另外,使用SetDoubleArrayRegion(),你引入了一个额外的memcopy;更好的是,使用double* cArray = GetDoubleArrayElements(env, jArray, 0),将值直接放入cArray,然后用ReleaseDoubleArrayElements(env, jArray, cArray, 0)释放到Java。 0 意味着对 cArray 的更改将在 jArray 中看到 Java边.

此外,Android NDK 提供了通过 #include "android/bitmap.h" 从 C 直接访问位图像素的功能。使用 getPixel()setPixel() 会增加巨大的开销。

在 C 端,您的崩溃很可能是因为 a[w][h][3] 在堆栈上的大位图分配失败。 Stack 不是为了保存巨大的数据块。您必须在堆中分配数组。

如果您切换到 C++ 并使用 std::vector() 和其他有用的快捷方式,您的代码可能会更简洁。