Android提高之使用NDK把彩图转换灰度图的方法

时间:2022-03-02 08:20:17

一般而言在android上使用java实现彩图转换为灰度图,与j2me上的实现方法类似,不过遇到频繁地转换或者是大图转换时,就必须使用ndk来提高速度了。本文主要通过java和ndk这两种方式来分别实现彩图转换为灰度图,并给出速度的对比,供大家参考。

先来简单地介绍一下android的ndk使用步骤:

以ndk r4为例,或许以后新版的ndk的使用方法略有不同。
1、下载支持c++的android-ndk-r4-crystax,支持c++的话可玩性更强。

2、下载cygwin,选择ftp://mirrors.kernel.org这个镜像,搜索  devel install 安装 gcc 和 make 等工具;

如图所示:

Android提高之使用NDK把彩图转换灰度图的方法

在搜索框里分别搜索gcc和make,必须是 devel install 栏的。

3、cygwin安装目录下,找到home/username的目录下的.bash_profile文件,打开文件在最后加上:
    ndk=/cygdrive/d:cygwin/android-ndk-r4-crystax
   export ndk
ps:假设安装在d:/cygwin/android-ndk-r4-crystax。
4、运行cygwin,通过cd命令去到ndk/samples/例子目录/,运行$ndk/ndk-build来编译该目录下的android.mk

以下是个人习惯

5、安装eclipse的cdt,官方下载cdt安装包,解压缩后把plugins和feagures 复制覆盖到eclipse文件夹下即可

6、去到系统属性->环境变量->path添加"d:/cygwin/bin"(假设cygwin安装在d:下)和"d:/cygwin/android-ndk-r4-crystax",重启计算机,然后就可以在eclipse里面建立基于cygwin的c/c++工程了,先通过这一步来验证ndk的程序能够编译成功,然后再通过第4步来生成so文件。

接下来看看本文程序运行的效果:

Android提高之使用NDK把彩图转换灰度图的方法

从转换灰度图的耗时来说,ndk的确比java所用的时间短不少。

main.xml源码如下:

?
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8" ?> 
- <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">
<button android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/btnjava" android:text="使用java转换灰度图" /> 
<button android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/btnndk" android:text="使用ndk转换灰度图" /> 
<imageview android:id="@+id/imageview01" android:layout_width="fill_parent" android:layout_height="fill_parent" /> 
</linearlayout>

主程序testtogray.java的源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.testtogray;
import android.app.activity;
import android.graphics.bitmap;
import android.graphics.bitmap.config;
import android.graphics.drawable.bitmapdrawable;
import android.os.bundle;
import android.view.view;
import android.widget.button;
import android.widget.imageview;
public class testtogray extends activity {
  /** called when the activity is first created. */
  button btnjava,btnndk;
  imageview imgview;
  @override
  public void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.main);
    this.settitle("使用ndk转换灰度图---hellogv");
    btnjava=(button)this.findviewbyid(r.id.btnjava);
    btnjava.setonclicklistener(new clickevent());
     
    btnndk=(button)this.findviewbyid(r.id.btnndk);
    btnndk.setonclicklistener(new clickevent());
    imgview=(imageview)this.findviewbyid(r.id.imageview01);
  }
  class clickevent implements view.onclicklistener{
    @override
    public void onclick(view v) {
      if(v==btnjava)
      {
        long current=system.currenttimemillis();
        bitmap img=convertgrayimg(r.drawable.cat);
        long performance=system.currenttimemillis()-current;
        //显示灰度图
        imgview.setimagebitmap(img);
        testtogray.this.settitle("w:"+string.valueof(img.getwidth())+",h:"+string.valueof(img.getheight())
            +" java耗时 "+string.valueof(performance)+" 毫秒");
      }
      else if(v==btnndk)
      {
        long current=system.currenttimemillis();
        //先打开图像并读取像素
        bitmap img1=((bitmapdrawable) getresources().getdrawable(r.drawable.cat)).getbitmap();
        int w=img1.getwidth(),h=img1.getheight();
        int[] pix = new int[w * h];
        img1.getpixels(pix, 0, w, 0, 0, w, h);
        //通过imgtogray.so把彩色像素转为灰度像素
        int[] resultint=libfuns.imgtogray(pix, w, h);
        bitmap resultimg=bitmap.createbitmap(w, h, config.rgb_565);
        resultimg.setpixels(resultint, 0, w, 0, 0,w, h);
        long performance=system.currenttimemillis()-current;
        //显示灰度图
        imgview.setimagebitmap(resultimg);
        testtogray.this.settitle("w:"+string.valueof(img1.getwidth())+",h:"+string.valueof(img1.getheight())
            +" ndk耗时 "+string.valueof(performance)+" 毫秒");
      }
    }
  }
   
  /**
   * 把资源图片转为灰度图
   * @param resid 资源id
   * @return
   */
  public bitmap convertgrayimg(int resid)
  {
    bitmap img1=((bitmapdrawable) getresources().getdrawable(resid)).getbitmap();
     
    int w=img1.getwidth(),h=img1.getheight();
    int[] pix = new int[w * h];
    img1.getpixels(pix, 0, w, 0, 0, w, h);
    int alpha=0xff<<24;
    for (int i = 0; i < h; i++) { 
      for (int j = 0; j < w; j++) { 
        // 获得像素的颜色 
        int color = pix[w * i + j]; 
        int red = ((color & 0x00ff0000) >> 16); 
        int green = ((color & 0x0000ff00) >> 8); 
        int blue = color & 0x000000ff
        color = (red + green + blue)/3
        color = alpha | (color << 16) | (color << 8) | color; 
        pix[w * i + j] = color;
      }
    }
    bitmap result=bitmap.createbitmap(w, h, config.rgb_565);
    result.setpixels(pix, 0, w, 0, 0,w, h);
    return result;
  }
}

封装ndk函数的java类libfuns.java的源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
package com.testtogray;
public class libfuns {
  static {
    system.loadlibrary("imgtogray");
  }
  /**
  * @param width the current view width
  * @param height the current view height
  */
  public static native int[] imgtogray(int[] buf, int w, int h);
}

彩图转换为灰度图的imgtogray.cpp源码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
extern "c" {
jniexport jintarray jnicall java_com_testtogray_libfuns_imgtogray(
    jnienv* env, jobject obj, jintarray buf, int w, int h);
}
;
jniexport jintarray jnicall java_com_testtogray_libfuns_imgtogray(
    jnienv* env, jobject obj, jintarray buf, int w, int h) {
  jint *cbuf;
  cbuf = env->getintarrayelements(buf, false);
  if (cbuf == null) {
    return 0; /* exception occurred */
  }
  int alpha = 0xff << 24;
  for (int i = 0; i < h; i++) {
    for (int j = 0; j < w; j++) {
      // 获得像素的颜色
      int color = cbuf[w * i + j];
      int red = ((color & 0x00ff0000) >> 16);
      int green = ((color & 0x0000ff00) >> 8);
      int blue = color & 0x000000ff;
      color = (red + green + blue) / 3;
      color = alpha | (color << 16) | (color << 8) | color;
      cbuf[w * i + j] = color;
    }
  }
  int size=w * h;
  jintarray result = env->newintarray(size);
  env->setintarrayregion(result, 0, size, cbuf);
  env->releaseintarrayelements(buf, cbuf, 0);
  return result;
}

android.mk的源码:

?
1
2
3
4
5
local_path:= $(call my-dir)
include $(clear_vars)
local_module  := imgtogray
local_src_files := imgtogray.cpp
include $(build_shared_library)

感兴趣的读者可以动手调试一下本文所述代码,相信会对大家进行android项目开发有一定的帮助。