Android图像处理简单例子

         简单的图像处理的学习例子,效果感觉不怎么好。关于算法,可以看之前的《 图像基本处理算法的简单实现》三篇文章。
         有人短我要jni库,直接把工程分享出来得了。之前主要是觉得实现不佳,并且重点再算法,所以没附工程==。
 
         再提示句,该工程没用bitmap.h。如果要直接传bitmap对象,可以看《 Android NDK基础样例》的样例3。
 
         内容太少,附点代码&截图^^。
 
1 )JoinImage.java

         Java native类,native接口&辅助方法。

 
  
  1. public class JoinImage { 
  2.  
  3.     static { 
  4.         System.loadLibrary("JoinImage"); 
  5.     } 
  6.  
  7.     /** LOG标识 */ 
  8.     private static final String TAG = "JoinImage"
  9.  
  10.     /** 图像存储路径 */ 
  11.     public static final String PATH = Environment.getExternalStorageDirectory() 
  12.             .toString() + "/" + TAG + "/"
  13.  
  14.     /** 
  15.      * 判断是否有SD卡 
  16.      */ 
  17.     public static boolean hasSDCard() { 
  18.         return Environment.getExternalStorageState().equals( 
  19.                 Environment.MEDIA_MOUNTED) ? true : false
  20.     } 
  21.  
  22.     /** 
  23.      * 保存图像为bitName.png 
  24.      */ 
  25.     public static void saveBitmap(String bitName, Bitmap mBitmap) { 
  26.  
  27.         // 不存在SD卡直接返回 
  28.         if (!hasSDCard()) { 
  29.             return
  30.         } 
  31.  
  32.         // 判断并创建图像存储路径 
  33.         File dirFile = new File(PATH); 
  34.         if (!dirFile.exists()) { 
  35.             dirFile.mkdir(); 
  36.         } 
  37.  
  38.         // 保存图像为高质量png 
  39.         FileOutputStream fOut = null
  40.         try { 
  41.             fOut = new FileOutputStream(PATH + bitName + ".png"); 
  42.             mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); 
  43.             fOut.flush(); 
  44.             fOut.close(); 
  45.         } catch (IOException e) { 
  46.             e.printStackTrace(); 
  47.         } 
  48.     } 
  49.  
  50.     /** 
  51.      * 缩放Bitmap图像并返回 
  52.      */ 
  53.     public static Bitmap stretch(Bitmap mBitmap, int newW, int newh) { 
  54.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  55.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  56.         colors = stretch(colors, w, h, newW, newh); // 调用动态库方法缩放图像返回颜色数组 
  57.         return createBitmap(colors, newW, newh); // 返回由新颜色数组重建的Bitmap 
  58.     } 
  59.  
  60.     /** 
  61.      * 灰度化Bitmap图像并返回 
  62.      */ 
  63.     public static Bitmap imgToGray(Bitmap mBitmap) { 
  64.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  65.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  66.         colors = imgToGray(colors, w, h); // 调用动态库方法灰度化颜色数组 
  67.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  68.     } 
  69.  
  70.     /** 
  71.      * 二值化灰度图像并返回 
  72.      */ 
  73.     public static Bitmap binarization(Bitmap mBitmap, int methodCode) { 
  74.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  75.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  76.         // 调用动态库方法二值化灰度图像 
  77.         switch (methodCode) { 
  78.         case 0
  79.             colors = binarization(colors, w, h); 
  80.             break
  81.         case 1
  82.             colors = binarization2(colors, w, h); 
  83.             break
  84.         default
  85.             throw new IllegalArgumentException("请选择正确的二值方法,现有0或1。"); 
  86.         } 
  87.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  88.     } 
  89.  
  90.     /** 
  91.      * 填充二值化图像并返回 
  92.      *  
  93.      * 填充方式:背景色点上下左右>=3点为前景色,则将其填充为前景色 
  94.      */ 
  95.     public static Bitmap filling(Bitmap mBitmap) { 
  96.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  97.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  98.         colors = filling(colors, w, h); // 调用动态库方法膨胀二值化图像 
  99.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  100.     } 
  101.  
  102.     /** 
  103.      * 膨胀二值化图像并返回 
  104.      *  
  105.      * 膨胀结构元素:3x3 全 
  106.      */ 
  107.     public static Bitmap dilation(Bitmap mBitmap) { 
  108.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  109.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  110.         colors = dilation(colors, w, h); // 调用动态库方法膨胀二值化图像 
  111.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  112.     } 
  113.  
  114.     /** 
  115.      * 腐蚀二值化图像并返回 
  116.      *  
  117.      * 腐蚀结构元素:3x3 全 
  118.      */ 
  119.     public static Bitmap erosion(Bitmap mBitmap) { 
  120.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  121.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  122.         colors = erosion(colors, w, h); // 调用动态库方法腐蚀二值化图像 
  123.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  124.     } 
  125.  
  126.     /** 
  127.      * 腐蚀二值化图像并返回 
  128.      *  
  129.      * 腐蚀结构元素:3x3 全 
  130.      */ 
  131.     public static Bitmap erosion(Bitmap mBitmap, int iterations) { 
  132.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  133.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  134.         while (iterations-- > 0) { 
  135.             colors = erosion(colors, w, h); // 调用动态库方法腐蚀二值化图像 
  136.         } 
  137.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  138.     } 
  139.  
  140.     /** 
  141.      * 细化二值化图像并返回 
  142.      */ 
  143.     public static Bitmap thinning(Bitmap mBitmap, int methodCode) { 
  144.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  145.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  146.         // 调用动态库方法细化二值化图像 
  147.         switch (methodCode) { 
  148.         case 0
  149.             colors = thinning(colors, w, h); 
  150.             break
  151.         case 1
  152.             colors = thinning2(colors, w, h); 
  153.             break
  154.         default
  155.             throw new IllegalArgumentException("请选择正确的细化方法,现有0或1。"); 
  156.         } 
  157.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  158.     } 
  159.  
  160.     /** 
  161.      * 分割细化图像 
  162.      */ 
  163.     public static void split(Bitmap mBitmap) { 
  164.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  165.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  166.         split(colors, w, h); // 调用动态库方法分割细化图像 
  167.     } 
  168.  
  169.     /** 
  170.      * 获取指定索引的分割图像 
  171.      */ 
  172.     public static Bitmap getSplitBmp(int index) { 
  173.         // 调用动态库方法获取指定索引分割图像颜色数组 
  174.         int[] colors = getSplitImg(index); 
  175.         if (null == colors) { 
  176.             throw new IllegalStateException("请确认已执行了分割图像,并且索引未越界。"); 
  177.         } 
  178.         // 调用动态库方法获取指定索引分割图像的长宽,并返回由新颜色数组重建的Bitmap 
  179.         return createBitmap(colors, getSplitImgW(index), getSplitImgH(index)); 
  180.     } 
  181.  
  182.     /** 
  183.      * 获取所有分割图像 
  184.      */ 
  185.     public static Bitmap[] getSplitBmps() { 
  186.         int num = getSplitNum(); // 调用动态库方法获取分割图像个数 
  187.         Bitmap bitmap[] = new Bitmap[num]; 
  188.         for (int i = 0; i < num; i++) { 
  189.             bitmap[i] = getSplitBmp(i); 
  190.         } 
  191.         return bitmap; 
  192.     } 
  193.  
  194.     /** 
  195.      * 解析识别分割图像 
  196.      *  
  197.      * mBitmap:单个字符的二值化图像 
  198.      */ 
  199.     public static char analyseImg(Bitmap mBitmap) { 
  200.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  201.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  202.         return analyseImg(colors, w, h); // 调用动态库方法解析识别分割图像 
  203.     } 
  204.  
  205.     /** 
  206.      * 二值化身份证号码彩图 
  207.      */ 
  208.     public static Bitmap binaryCid(Bitmap mBitmap) { 
  209.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  210.         int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组 
  211.         colors = binaryCid(colors, w, h); // 调用动态库方法二值化彩图身份证号码 
  212.         return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap 
  213.     } 
  214.  
  215.     /** 
  216.      * 获取Bitmap颜色数组 
  217.      */ 
  218.     public static int[] getColors(Bitmap mBitmap, int w, int h) { 
  219.         int[] pix = new int[w * h]; 
  220.         mBitmap.getPixels(pix, 0, w, 00, w, h); 
  221.         return pix; 
  222.     } 
  223.  
  224.     /** 
  225.      * 由颜色数组重建Bitmap 
  226.      */ 
  227.     public static Bitmap createBitmap(int[] colors, int w, int h) { 
  228.         Bitmap img = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); 
  229.         img.setPixels(colors, 0, w, 00, w, h); 
  230.         return img; 
  231.     } 
  232.  
  233.     public static String getTime() { 
  234.         return String.valueOf(System.currentTimeMillis()); 
  235.     } 
  236.  
  237.     public void sayHello(String msg) { 
  238.         Log.e("C调用Java", msg); 
  239.     } 
  240.  
  241.     public static native int[] stretch(int[] buf, int srcW, int srcH, int dstW, 
  242.             int dstH); 
  243.  
  244.     public static native int[] imgToGray(int[] buf, int w, int h); 
  245.  
  246.     public static native int[] binarization(int[] buf, int w, int h); 
  247.  
  248.     public static native int[] binarization2(int[] buf, int w, int h); 
  249.  
  250.     public static native int[] filling(int[] buf, int w, int h); 
  251.  
  252.     public static native int[] dilation(int[] buf, int w, int h); 
  253.  
  254.     public static native int[] erosion(int[] buf, int w, int h); 
  255.  
  256.     public static native int[] thinning(int[] buf, int w, int h); 
  257.  
  258.     public static native int[] thinning2(int[] buf, int w, int h); 
  259.  
  260.     public static native void split(int[] buf, int w, int h); 
  261.  
  262.     public static native int getSplitNum(); 
  263.  
  264.     public static native int[] getSplitImg(int index); 
  265.  
  266.     public static native int getSplitImgW(int index); 
  267.  
  268.     public static native int getSplitImgH(int index); 
  269.  
  270.     public static native char analyseImg(int[] buf, int w, int h); 
  271.  
  272.     public static native int[] locateCid(int[] buf, int w, int h); 
  273.  
  274.     public static native int[] binaryCid(int[] buf, int w, int h); 
  275.  
 
2 )ImageActivity.java
         Activity类,界面&处理逻辑。
 
 
  
  1. public class ImageActivity extends Activity implements OnClickListener, 
  2.         OnItemClickListener { 
  3.  
  4.     private Button[] buttons; // 按钮组 
  5.     private int enabledBtnIndex; // 当前可用按钮索引 
  6.  
  7.     private ImageView imageView; // ImageView 
  8.     private Bitmap cardBitmap; // 获取的图像 
  9.  
  10.     private GridView gridView; // GridView 
  11.     private Bitmap splitBmps[]; // 分割后的图像 
  12.  
  13.     private boolean isSave = false// 是否保存图像(各处理步骤) 
  14.     private int erosionCount = 1// 腐蚀次数 
  15.  
  16.     @Override 
  17.     public void onCreate(Bundle savedInstanceState) { 
  18.         super.onCreate(savedInstanceState); 
  19.         setContentView(R.layout.main); 
  20.  
  21.         // 各属性&组件初始化......
  22.     } 
  23.  
  24.     /** 
  25.      * 按钮点击事件 
  26.      */ 
  27.     @Override 
  28.     public void onClick(View v) { 
  29.         // 先设置当前按钮不可用,避免多次点击 
  30.         buttons[enabledBtnIndex].setEnabled(false); 
  31.         switch (v.getId()) { 
  32.         case R.id.takeBtn: { 
  33.  
  34.             // 列表项目 
  35.             final String[] items = { "330122198102212239"
  36.                     "15210319861215033X""370305196708031216"
  37.                     "210203196809236015""411224196902244239" }; 
  38.             // 对应资源id 
  39.             // 图像1、2符合像素要求(宽度>= 1000),后续处理不会过度 
  40.             // 图像3像素折中(800<宽度<1000),腐蚀2次会过度(固定死2次腐蚀,可测试不完整识别) 
  41.             // 图像4像素过低(宽度<=800),用于测试缩放图像后继续处理效果 
  42.             // 图像5像素过低且光照较暗不均匀(宽度<=400),用于测试缩放图像及后续处理效果 
  43.             final int[] ids = { R.drawable.idcard_1, R.drawable.idcard_2, 
  44.                     R.drawable.idcard_3, R.drawable.idcard_4, 
  45.                     R.drawable.idcard_5 }; 
  46.             // 显示列表对话框 
  47.             new AlertDialog.Builder(this
  48.                     .setTitle("选择内置身份证号码"
  49.                     .setItems(items, new DialogInterface.OnClickListener() { 
  50.                         public void onClick(DialogInterface dialog, int which) { 
  51.                             // 获取图像资源 
  52.                             InputStream is = getResources().openRawResource( 
  53.                                     ids[which]); 
  54.                             BitmapDrawable bmpDraw = new BitmapDrawable(is); 
  55.                             // 判断图像像素修正图像 
  56.                             cardBitmap = correctBitmap(bmpDraw.getBitmap()); 
  57.                             // 设置显示图像 
  58.                             imageView.setImageBitmap(cardBitmap); 
  59.                             // 显示ImageView 
  60.                             imageView.setVisibility(View.VISIBLE); 
  61.                             // 隐藏GridView 
  62.                             gridView.setVisibility(View.GONE); 
  63.                             // 释放缓存图像 
  64.                             splitBmps = null
  65.                             // 启用下一按钮 
  66.                             setNextBtnEnabled(); 
  67.                         } 
  68.                     }) 
  69.                     .setPositiveButton("或拍照取像"
  70.                             new DialogInterface.OnClickListener() { 
  71.                                 @Override 
  72.                                 public void onClick(DialogInterface dialog, 
  73.                                         int which) { 
  74.                                     // 拍照按钮设为可用 
  75.                                     buttons[enabledBtnIndex].setEnabled(true); 
  76.                                     // 调用自定义相机 
  77.                                     Intent intent = new Intent( 
  78.                                             getApplicationContext(), 
  79.                                             CameraActivity.class); 
  80.                                     startActivityForResult(intent, 1); 
  81.                                 } 
  82.                             }).setCancelable(false).show(); 
  83.             break
  84.         } 
  85.         case R.id.toGraybtn: { 
  86.             // 灰度化图像(将图像变为灰度图像) 
  87.             cardBitmap = JoinImage.imgToGray(cardBitmap); 
  88.             imageView.setImageBitmap(cardBitmap); 
  89.             // 保存该图像 
  90.             if (isSave) { 
  91.                 JoinImage.saveBitmap("1_gray", cardBitmap); 
  92.             } 
  93.             // 启用下一按钮 
  94.             setNextBtnEnabled(); 
  95.             break
  96.         } 
  97.         case R.id.toBinaBtn: { 
  98.             // 二值化图像(将图像变为纯黑白,灰度值仅有0和255,方便后续处理) 
  99.             cardBitmap = JoinImage.binarization(cardBitmap, 0); 
  100.             imageView.setImageBitmap(cardBitmap); 
  101.             // 保存该图像 
  102.             if (isSave) { 
  103.                 JoinImage.saveBitmap("2_bina", cardBitmap); 
  104.             } 
  105.             // 启用下一按钮 
  106.             setNextBtnEnabled(); 
  107.             break
  108.         } 
  109.         case R.id.toFillBtn: { 
  110.             // 填充图像(算是膨胀的变形,和膨胀作用一致) 
  111.             cardBitmap = JoinImage.filling(cardBitmap); 
  112.             imageView.setImageBitmap(cardBitmap); 
  113.             // 保存该图像 
  114.             if (isSave) { 
  115.                 JoinImage.saveBitmap("3_fill", cardBitmap); 
  116.             } 
  117.             // 启用下一按钮 
  118.             setNextBtnEnabled(); 
  119.             break
  120.         } 
  121.         case R.id.toDilaBtn: { 
  122.             // 膨胀图像(用于填充图像中间可能出现的单个像素点,以防腐蚀后出一个窟窿) 
  123.             cardBitmap = JoinImage.dilation(cardBitmap); 
  124.             imageView.setImageBitmap(cardBitmap); 
  125.             // 保存该图像 
  126.             if (isSave) { 
  127.                 JoinImage.saveBitmap("4_dila", cardBitmap); 
  128.             } 
  129.             // 启用下一按钮 
  130.             setNextBtnEnabled(); 
  131.             break
  132.         } 
  133.         case R.id.toErosBtn: { 
  134.             // 腐蚀图像(2次,使得边缘更平滑,减少细化后突出骨架,同时可去除噪点) 
  135.             // 图像像素比较低时可能会腐蚀过度。现需要图像宽度至少800,过低像素做好缩放至宽度1000。 
  136.             cardBitmap = JoinImage.erosion(cardBitmap, erosionCount); 
  137.             imageView.setImageBitmap(cardBitmap); 
  138.             // 保存该图像 
  139.             if (isSave) { 
  140.                 JoinImage.saveBitmap("5_eros", cardBitmap); 
  141.             } 
  142.             // 启用下一按钮 
  143.             setNextBtnEnabled(); 
  144.             break
  145.         } 
  146.         case R.id.toThinBtn: { 
  147.             // 细化图像(提取图像的骨架特征,方便分析) 
  148.             cardBitmap = JoinImage.thinning(cardBitmap, 1); 
  149.             imageView.setImageBitmap(cardBitmap); 
  150.             // 保存该图像 
  151.             if (isSave) { 
  152.                 JoinImage.saveBitmap("6_thin", cardBitmap); 
  153.             } 
  154.             // 启用下一按钮 
  155.             setNextBtnEnabled(); 
  156.             break
  157.         } 
  158.         case R.id.toSplitBtn: { 
  159.             // 分割图像(对细化图像进行分割) 
  160.             JoinImage.split(cardBitmap); 
  161.             // 获取所有分割图像 
  162.             splitBmps = JoinImage.getSplitBmps(); 
  163.             // 添加GirdView数据 
  164.             gridView.setAdapter(new ImageAdapter(this, splitBmps)); 
  165.             // 隐藏ImageView 
  166.             imageView.setVisibility(View.GONE); 
  167.             // 显示GirdView 
  168.             gridView.setVisibility(View.VISIBLE); 
  169.             // 释放缓存图像 
  170.             cardBitmap = null
  171.             // 保存该图像 
  172.             if (isSave && null != splitBmps) { 
  173.                 for (int i = 0; i < splitBmps.length; i++) { 
  174.                     JoinImage.saveBitmap("split_" + i, splitBmps[i]); 
  175.                 } 
  176.             } 
  177.             // 启用下一按钮 
  178.             setNextBtnEnabled(); 
  179.             break
  180.         } 
  181.         } 
  182.     } 
  183.  
  184.     /** 
  185.      * 图像修正并设置腐蚀次数 
  186.      */ 
  187.     private Bitmap correctBitmap(Bitmap mBitmap) { 
  188.         int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽 
  189.         if (w >= 1500) { 
  190.             erosionCount = 2// 腐蚀次数置2 
  191.             int newW = 1000// 缩放图像至宽度1000 
  192.             int newH = h * newW / w; // 高度等比例变化 
  193.             // 缩放图像 
  194.             Bitmap newBitmap = JoinImage.stretch(mBitmap, newW, newH); 
  195.             // 保存该图像 
  196.             if (isSave) { 
  197.                 JoinImage.saveBitmap("0_stre", newBitmap); 
  198.             } 
  199.             return newBitmap; 
  200.         } else if (w >= 1000) { 
  201.             erosionCount = 2// 腐蚀次数置2 
  202.             return mBitmap; 
  203.         } else if (w <= 800) { 
  204.             erosionCount = 2// 腐蚀次数置2 
  205.             if (w <= 400) { 
  206.                 Log.i("图像修正""图像像素过低!"); 
  207.             } 
  208.             int newW = 1000// 缩放图像至宽度1000 
  209.             int newH = h * newW / w; // 高度等比例变化 
  210.             // 缩放图像 
  211.             Bitmap newBitmap = JoinImage.stretch(mBitmap, newW, newH); 
  212.             // 保存该图像 
  213.             if (isSave) { 
  214.                 JoinImage.saveBitmap("0_stre", newBitmap); 
  215.             } 
  216.             return newBitmap; 
  217.         } else { 
  218.             erosionCount = 1// 腐蚀次数置1 
  219.             return mBitmap; 
  220.         } 
  221.     } 
  222.  
  223.     /** 
  224.      * GridView点击事件 
  225.      */ 
  226.     @Override 
  227.     public void onItemClick(AdapterView<?> parent, View view, int position, 
  228.             long id) { 
  229.         Toast.makeText(this"->" + JoinImage.analyseImg(splitBmps[position]), 
  230.                 Toast.LENGTH_SHORT).show(); 
  231.     } 
  232.  
  233.     ......
  234.  
 
3 )截图
 
主界面  主界面  主界面  主界面

二值化 二值化 二值化 二值化

分割 分割

 
4 )后记
         不足之处,多多担待^^。