【问题标题】:implement water color effect on image using JNI使用JNI对图像实现水彩效果
【发布时间】:2012-11-24 06:24:28
【问题描述】:

我在 android 中为图像上的水彩效果实现了一些代码,但速度很慢(需要超过 2 分钟)现在我尝试在 JNI 中实现它以提高连击速度, 听到是我的

的 java 代码

inPixels 是 Bitmap 的像素。

 protected int[] filterPixels( int width, int height, int[] inPixels ) 
    {
        int levels = 256;
        int index = 0;

        int[] rHistogram = new int[levels];
        int[] gHistogram = new int[levels];
        int[] bHistogram = new int[levels];
        int[] rTotal = new int[levels];
        int[] gTotal = new int[levels];
        int[] bTotal = new int[levels];
        int[] outPixels = new int[width * height];

        for (int y = 0; y < height; y++) 
        {
            for (int x = 0; x < width; x++) 
            {
                for (int i = 0; i < levels; i++)
                    rHistogram[i] = gHistogram[i] = bHistogram[i] = rTotal[i] = gTotal[i] = bTotal[i] = 0;

                for (int row = -range; row <= range; row++) 
                {
                    int iy = y+row;
                    int ioffset;
                    if (0 <= iy && iy < height) 
                    {
                        ioffset = iy*width;
                        for (int col = -range; col <= range; col++) 
                        {
                            int ix = x+col;
                            if (0 <= ix && ix < width) {
                                int rgb = inPixels[ioffset+ix];
                                int r = (rgb >> 16) & 0xff;
                                int g = (rgb >> 8) & 0xff;
                                int b = rgb & 0xff;
                                int ri = r*levels/256;
                                int gi = g*levels/256;
                                int bi = b*levels/256;
                                rTotal[ri] += r;
                                gTotal[gi] += g;
                                bTotal[bi] += b;
                                rHistogram[ri]++;
                                gHistogram[gi]++;
                                bHistogram[bi]++;
                            }
                        }
                    }
                }

                int r = 0, g = 0, b = 0;
                for (int i = 1; i < levels; i++) 
                {
                    if (rHistogram[i] > rHistogram[r])
                        r = i;
                    if (gHistogram[i] > gHistogram[g])
                        g = i;
                    if (bHistogram[i] > bHistogram[b])
                        b = i;
                }
                r = rTotal[r] / rHistogram[r];
                g = gTotal[g] / gHistogram[g];
                b = bTotal[b] / bHistogram[b];
                outPixels[index] = (inPixels[index] & 0xff000000) | ( r << 16 ) | ( g << 8 ) | b;
                index++;
            }
        }

        return outPixels;
    }

**输出图像**

我尝试将此 java 代码转换为 c 代码,但我不知道有什么问题, 听到C的代码

 void filterPixels( int width, int height, int inPixels[] )
    {



        int levels = 256;
        int index = 0;

        int rHistogram [levels];
        int gHistogram [levels];
        int bHistogram [levels];
        int rTotal   [levels];
        int gTotal [levels];
        int bTotal [levels];
        int outPixels [width * height];

        //Loop Variables
        int y ;
        int x ;
        int i ;
        int row ;
        int col ;
        int j ;

        int range = 5 ;

        for ( y = 0; y < height; y++)
        {
            for ( x = 0; x < width; x++)
            {
                for ( i = 0; i < levels; i++)
                    rHistogram[i] = gHistogram[i] = bHistogram[i] = rTotal[i] = gTotal[i] = bTotal[i] = 0;

                for ( row = -range; row <= range; row++)
                {
                    int iy = y+row;
                    int ioffset;
                    if (0 <= iy && iy < height)
                    {
                        ioffset = iy*width;
                        for ( col = -range; col <= range; col++)
                        {
                            int ix = x+col;
                            if (0 <= ix && ix < width) {
                                int rgb = inPixels[ioffset+ix];
                                int r = (rgb >> 16) & 0xff;
                                int g = (rgb >> 8) & 0xff;
                                int b = rgb & 0xff;
                                int ri = r*levels/256;
                                int gi = g*levels/256;
                                int bi = b*levels/256;
                                rTotal[ri] += r;
                                gTotal[gi] += g;
                                bTotal[bi] += b;
                                rHistogram[ri]++;
                                gHistogram[gi]++;
                                bHistogram[bi]++;
                            }
                        }
                    }
                }

                int r = 0, g = 0, b = 0;
                for ( j = 1; j < levels; j++)
                {
                    if (rHistogram[j] > rHistogram[r])
                        r = j;
                    if (gHistogram[j] > gHistogram[g])
                        g = j;
                    if (bHistogram[j] > bHistogram[b])
                        b = j;
                }
                r = rTotal[r] / rHistogram[r];
                g = gTotal[g] / gHistogram[g];
                b = bTotal[b] / bHistogram[b];
                outPixels[index] = (inPixels[index] & 0xff000000) | ( r << 16 ) | ( g << 8 ) | b;
                index++;
            }
        }
    }

我检查java 代码和c 代码的像素值是否相同(对于同一图像)

从我的 android 活动中调用本机函数的代码。

int[] pix = new int[oraginal.getWidth() * oraginal.getHeight()];

                Bitmap bitmap = oraginal.copy(oraginal.getConfig(), true);
                bitmap.getPixels(pix, 0, bitmap.getWidth(), 0, 0,bitmap.getWidth(), bitmap.getHeight());
                filterPixelsJNI(bitmap.getWidth(), bitmap.getHeight(), pix);

                 bitmap.setPixels(pix, 0, bitmap.getWidth(), 0, 0,bitmap.getWidth(), bitmap.getHeight());
                 myView.setImageBitmap(bitmap);

这是我第一次尝试 JNI,所以请帮助我。

更新

public native void filterPixelsJNI( int width, int height, int inPixels[] );

JNI

 JNIEXPORT void JNICALL Java_com_testndk_HelloWorldActivity_filterPixelsJNI (JNIEnv * env, jobject obj , jint width,jint height,jint inPixels[]){

     filterPixels( width, height, inPixels);
 }

filterPixels 方法是从 c 代码调用的。

【问题讨论】:

  • 究竟是什么不起作用?它会崩溃吗,它会返回黑色图像吗?
  • 不,它没有崩溃,但由于某种原因效果不适用我不知道有什么问题。
  • 能否添加 filterPixelsJNI() 的代码,我猜这是代码的 JNI 部分?
  • 调试时 filterPixels() 会发生什么?
  • @Goot the pix from filterPixels(c code) value 和 pix (from java code) value 是一样的,但我不知道效果是不显示..?

标签: android image-processing java-native-interface


【解决方案1】:

您的 JNI 代码存在几个问题。算法部分可能是正确的,但您没有正确处理 Java 数组到 C 数组的转换。

首先,Java_com_testndk_HelloWorldActivity_filterPixelsJNI 的最后一个参数的类型应该是jintArray,而不是jint []。这就是将 Java 数组传递给 C 代码的方式。

一旦你得到这个数组,你不能直接处理它,你必须把它转换成一个 C 数组:

JNIEXPORT void JNICALL Java_com_testndk_HelloWorldActivity_filterPixelsJNI (JNIEnv * env, jobject obj , jint width, jint height, jintArray inPixels) {

    int *c_inPixels = (*env)->GetIntArrayElements(env, inPixels, NULL);
    filterPixels( width, height, c_inPixels);
    // passing 0 as the last argument should copy native array to Java array
    (*env)->ReleaseIntArrayElements(env, inPixels, c_inPixels, 0);
}

我建议你看一下 JNI 文档,其中解释了如何处理数组:http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html

请注意,现在有更简单的方法可以使用 android NDK 处理 Java 位图对象。详情请见an other of my answers here

【讨论】:

  • 是的,你说得对,我按照你说的那样实现了,但效果仍然不适用于图像,你能告诉我如何将 outPixels[] 的值从 JNI 返回到 java。
  • 您可以使用jintArray outPixels = (*env)-&gt;NewIntArray(env, width*height) 创建一个新的Java 数组,使用(*env)-&gt;SetIntArrayRegion(env, outPixels, 0, width*height, c_inPixels) 将其填充为本机值,然后您只需将其返回即可。请记住,我在这里提供的代码的 sn-ps 可能并不完美,我还没有在您的上下文中测试它们,您可能需要稍微调整它们...... JNI 和 NDK 文档应该是您在这里的真正参考!
  • 是的,我发现问题是我的 outPixels 没有返回 pix 的值,所以我在 filterPixels 方法 jintArray joutPixels = (env)->NewIntArray(env, width i>高度); (env)->SetIntArrayRegion(env, joutPixels, 0, widthheight, outPixels);返回 joutPixels;谢谢..
  • 很抱歉问这个问题,当我尝试在图像(1440 * 2560)上应用它时它崩溃了。我认为问题是记忆你能建议我做什么吗?
  • 嗯,使用这种方法会导致位图内存被复制:它存在于Java和C中,确实必须使用大量内存。我认为一个解决方案可能是使用 Android NDK 的 Bitmap API 直接访问和修改 Java 像素缓冲区。您可能可以在此主题上找到一些示例代码,此处提供了另一种过滤器的示例:github.com/ruckus/android-image-filter-ndk/blob/master/jni/…(JNI 函数与您尝试执行的操作特别相关)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-12
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
  • 2016-05-02
  • 2010-12-29
相关资源
最近更新 更多