【问题标题】:jni - Slow processing in nativejni - 本机处理速度慢
【发布时间】:2016-06-02 10:48:57
【问题描述】:

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

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

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

【问题讨论】:

  • 您应该更具体地了解应用程序“崩溃”时会发生什么。您是否收到任何回溯或错误消息。至于慢,你应该学习如何分析你的代码以找出它花费最多时间的地方
  • Try this. 这不是找出哪里花费最多时间的问题,而是为什么

标签: c performance image-processing java-native-interface native


【解决方案1】:

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

同样适用于输出数组。

另外,使用SetDoubleArrayRegion(),你引入了一个额外的内存拷贝;更好的是,使用double* cArray = GetDoubleArrayElements(env, jArray, 0),将值直接放入cArray,然后使用ReleaseDoubleArrayElements(env, jArray, cArray, 0) 将其发布到Java。这个0表示cArray的变化会在Java端的jArray中看到。

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

在 C 方面,很可能发生崩溃是因为堆栈上的 a[w][h][3] 的大位图分配失败。堆栈并不打算保存巨大的数据块。你必须在堆中分配你的数组。

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

【讨论】:

  • 我以前写过这些技巧;您是否尝试过它们,发现它们并没有给您带来预期的提升?或者您不了解如何遵循它们,需要更多指导?
  • 1-对于第一个技巧,我更愿意保留 [ ][ ][ ] 模式,因为我要实现一个需要许多 3D 循环的算法... 2- 我没有明白你的意思:你能解释更多吗? 3- 对于 bitmap.h,我不知道如何提取像素。它可以完全像android的一样使用吗?将图像作为数组传递会更好吗?谢谢顺便说一句:)
  • 1. 访问“平面”3D 数组比任何语言的数组数组的数组都要高效得多。 C 语言具有您在代码中使用的欺骗性功能:您可以在堆栈上分配 double a[w][h][3] 作为有效的连续数组,或者使用 double*** b 并在堆中分配 3 维非连续结构。访问 a[x][y][c]b[x][y][c] 高效得多,但堆栈是非常有限的资源。很可能,您的崩溃发生是因为对于大位图,此分配失败。
  • 2. 除了访问上面提到的数组内存之外,还有另一个权衡:您希望最小化复制像素内存的数量。最好的方法是加载位图,锁定其像素(即获取指向第一个像素的指针),并就地使用它们,而不复制数据。 SetDoubleArrayRegion() 不能在原地工作,GetDoubleArrayElements() 可以,并且在 Android 上(非合同行为)它可以在没有副本的情况下工作。
  • 3. 但是,如果您的源是 Android 位图,直接访问要快得多,而且它是专为像您这样的场景而设计的。 AndroidBitmap_lockPixels() 允许直接访问像素。你可以找到很多例子,例如ruckus.tumblr.com/post/18055652108/…
猜你喜欢
  • 2021-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-01
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 2020-09-04
相关资源
最近更新 更多