Android图像处理简单例子
简单的图像处理的学习例子,效果感觉不怎么好。关于算法,可以看之前的《
图像基本处理算法的简单实现》三篇文章。
有人短我要jni库,直接把工程分享出来得了。之前主要是觉得实现不佳,并且重点再算法,所以没附工程==。
再提示句,该工程没用bitmap.h。如果要直接传bitmap对象,可以看《
Android NDK基础样例》的样例3。
内容太少,附点代码&截图^^。
1
)JoinImage.java
Java native类,native接口&辅助方法。
- public class JoinImage {
- static {
- System.loadLibrary("JoinImage");
- }
- /** LOG标识 */
- private static final String TAG = "JoinImage";
- /** 图像存储路径 */
- public static final String PATH = Environment.getExternalStorageDirectory()
- .toString() + "/" + TAG + "/";
- /**
- * 判断是否有SD卡
- */
- public static boolean hasSDCard() {
- return Environment.getExternalStorageState().equals(
- Environment.MEDIA_MOUNTED) ? true : false;
- }
- /**
- * 保存图像为bitName.png
- */
- public static void saveBitmap(String bitName, Bitmap mBitmap) {
- // 不存在SD卡直接返回
- if (!hasSDCard()) {
- return;
- }
- // 判断并创建图像存储路径
- File dirFile = new File(PATH);
- if (!dirFile.exists()) {
- dirFile.mkdir();
- }
- // 保存图像为高质量png
- FileOutputStream fOut = null;
- try {
- fOut = new FileOutputStream(PATH + bitName + ".png");
- mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
- fOut.flush();
- fOut.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * 缩放Bitmap图像并返回
- */
- public static Bitmap stretch(Bitmap mBitmap, int newW, int newh) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- colors = stretch(colors, w, h, newW, newh); // 调用动态库方法缩放图像返回颜色数组
- return createBitmap(colors, newW, newh); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 灰度化Bitmap图像并返回
- */
- public static Bitmap imgToGray(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- colors = imgToGray(colors, w, h); // 调用动态库方法灰度化颜色数组
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 二值化灰度图像并返回
- */
- public static Bitmap binarization(Bitmap mBitmap, int methodCode) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- // 调用动态库方法二值化灰度图像
- switch (methodCode) {
- case 0:
- colors = binarization(colors, w, h);
- break;
- case 1:
- colors = binarization2(colors, w, h);
- break;
- default:
- throw new IllegalArgumentException("请选择正确的二值方法,现有0或1。");
- }
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 填充二值化图像并返回
- *
- * 填充方式:背景色点上下左右>=3点为前景色,则将其填充为前景色
- */
- public static Bitmap filling(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- colors = filling(colors, w, h); // 调用动态库方法膨胀二值化图像
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 膨胀二值化图像并返回
- *
- * 膨胀结构元素:3x3 全
- */
- public static Bitmap dilation(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- colors = dilation(colors, w, h); // 调用动态库方法膨胀二值化图像
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 腐蚀二值化图像并返回
- *
- * 腐蚀结构元素:3x3 全
- */
- public static Bitmap erosion(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- colors = erosion(colors, w, h); // 调用动态库方法腐蚀二值化图像
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 腐蚀二值化图像并返回
- *
- * 腐蚀结构元素:3x3 全
- */
- public static Bitmap erosion(Bitmap mBitmap, int iterations) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- while (iterations-- > 0) {
- colors = erosion(colors, w, h); // 调用动态库方法腐蚀二值化图像
- }
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 细化二值化图像并返回
- */
- public static Bitmap thinning(Bitmap mBitmap, int methodCode) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- // 调用动态库方法细化二值化图像
- switch (methodCode) {
- case 0:
- colors = thinning(colors, w, h);
- break;
- case 1:
- colors = thinning2(colors, w, h);
- break;
- default:
- throw new IllegalArgumentException("请选择正确的细化方法,现有0或1。");
- }
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 分割细化图像
- */
- public static void split(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- split(colors, w, h); // 调用动态库方法分割细化图像
- }
- /**
- * 获取指定索引的分割图像
- */
- public static Bitmap getSplitBmp(int index) {
- // 调用动态库方法获取指定索引分割图像颜色数组
- int[] colors = getSplitImg(index);
- if (null == colors) {
- throw new IllegalStateException("请确认已执行了分割图像,并且索引未越界。");
- }
- // 调用动态库方法获取指定索引分割图像的长宽,并返回由新颜色数组重建的Bitmap
- return createBitmap(colors, getSplitImgW(index), getSplitImgH(index));
- }
- /**
- * 获取所有分割图像
- */
- public static Bitmap[] getSplitBmps() {
- int num = getSplitNum(); // 调用动态库方法获取分割图像个数
- Bitmap bitmap[] = new Bitmap[num];
- for (int i = 0; i < num; i++) {
- bitmap[i] = getSplitBmp(i);
- }
- return bitmap;
- }
- /**
- * 解析识别分割图像
- *
- * mBitmap:单个字符的二值化图像
- */
- public static char analyseImg(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- return analyseImg(colors, w, h); // 调用动态库方法解析识别分割图像
- }
- /**
- * 二值化身份证号码彩图
- */
- public static Bitmap binaryCid(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- int[] colors = getColors(mBitmap, w, h); // 获取Bitmap颜色数组
- colors = binaryCid(colors, w, h); // 调用动态库方法二值化彩图身份证号码
- return createBitmap(colors, w, h); // 返回由新颜色数组重建的Bitmap
- }
- /**
- * 获取Bitmap颜色数组
- */
- public static int[] getColors(Bitmap mBitmap, int w, int h) {
- int[] pix = new int[w * h];
- mBitmap.getPixels(pix, 0, w, 0, 0, w, h);
- return pix;
- }
- /**
- * 由颜色数组重建Bitmap
- */
- public static Bitmap createBitmap(int[] colors, int w, int h) {
- Bitmap img = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
- img.setPixels(colors, 0, w, 0, 0, w, h);
- return img;
- }
- public static String getTime() {
- return String.valueOf(System.currentTimeMillis());
- }
- public void sayHello(String msg) {
- Log.e("C调用Java", msg);
- }
- public static native int[] stretch(int[] buf, int srcW, int srcH, int dstW,
- int dstH);
- public static native int[] imgToGray(int[] buf, int w, int h);
- public static native int[] binarization(int[] buf, int w, int h);
- public static native int[] binarization2(int[] buf, int w, int h);
- public static native int[] filling(int[] buf, int w, int h);
- public static native int[] dilation(int[] buf, int w, int h);
- public static native int[] erosion(int[] buf, int w, int h);
- public static native int[] thinning(int[] buf, int w, int h);
- public static native int[] thinning2(int[] buf, int w, int h);
- public static native void split(int[] buf, int w, int h);
- public static native int getSplitNum();
- public static native int[] getSplitImg(int index);
- public static native int getSplitImgW(int index);
- public static native int getSplitImgH(int index);
- public static native char analyseImg(int[] buf, int w, int h);
- public static native int[] locateCid(int[] buf, int w, int h);
- public static native int[] binaryCid(int[] buf, int w, int h);
- }
2
)ImageActivity.java
Activity类,界面&处理逻辑。
- public class ImageActivity extends Activity implements OnClickListener,
- OnItemClickListener {
- private Button[] buttons; // 按钮组
- private int enabledBtnIndex; // 当前可用按钮索引
- private ImageView imageView; // ImageView
- private Bitmap cardBitmap; // 获取的图像
- private GridView gridView; // GridView
- private Bitmap splitBmps[]; // 分割后的图像
- private boolean isSave = false; // 是否保存图像(各处理步骤)
- private int erosionCount = 1; // 腐蚀次数
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 各属性&组件初始化......
- }
- /**
- * 按钮点击事件
- */
- @Override
- public void onClick(View v) {
- // 先设置当前按钮不可用,避免多次点击
- buttons[enabledBtnIndex].setEnabled(false);
- switch (v.getId()) {
- case R.id.takeBtn: {
- // 列表项目
- final String[] items = { "330122198102212239",
- "15210319861215033X", "370305196708031216",
- "210203196809236015", "411224196902244239" };
- // 对应资源id
- // 图像1、2符合像素要求(宽度>= 1000),后续处理不会过度
- // 图像3像素折中(800<宽度<1000),腐蚀2次会过度(固定死2次腐蚀,可测试不完整识别)
- // 图像4像素过低(宽度<=800),用于测试缩放图像后继续处理效果
- // 图像5像素过低且光照较暗不均匀(宽度<=400),用于测试缩放图像及后续处理效果
- final int[] ids = { R.drawable.idcard_1, R.drawable.idcard_2,
- R.drawable.idcard_3, R.drawable.idcard_4,
- R.drawable.idcard_5 };
- // 显示列表对话框
- new AlertDialog.Builder(this)
- .setTitle("选择内置身份证号码")
- .setItems(items, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // 获取图像资源
- InputStream is = getResources().openRawResource(
- ids[which]);
- BitmapDrawable bmpDraw = new BitmapDrawable(is);
- // 判断图像像素修正图像
- cardBitmap = correctBitmap(bmpDraw.getBitmap());
- // 设置显示图像
- imageView.setImageBitmap(cardBitmap);
- // 显示ImageView
- imageView.setVisibility(View.VISIBLE);
- // 隐藏GridView
- gridView.setVisibility(View.GONE);
- // 释放缓存图像
- splitBmps = null;
- // 启用下一按钮
- setNextBtnEnabled();
- }
- })
- .setPositiveButton("或拍照取像",
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- // 拍照按钮设为可用
- buttons[enabledBtnIndex].setEnabled(true);
- // 调用自定义相机
- Intent intent = new Intent(
- getApplicationContext(),
- CameraActivity.class);
- startActivityForResult(intent, 1);
- }
- }).setCancelable(false).show();
- break;
- }
- case R.id.toGraybtn: {
- // 灰度化图像(将图像变为灰度图像)
- cardBitmap = JoinImage.imgToGray(cardBitmap);
- imageView.setImageBitmap(cardBitmap);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("1_gray", cardBitmap);
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- case R.id.toBinaBtn: {
- // 二值化图像(将图像变为纯黑白,灰度值仅有0和255,方便后续处理)
- cardBitmap = JoinImage.binarization(cardBitmap, 0);
- imageView.setImageBitmap(cardBitmap);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("2_bina", cardBitmap);
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- case R.id.toFillBtn: {
- // 填充图像(算是膨胀的变形,和膨胀作用一致)
- cardBitmap = JoinImage.filling(cardBitmap);
- imageView.setImageBitmap(cardBitmap);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("3_fill", cardBitmap);
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- case R.id.toDilaBtn: {
- // 膨胀图像(用于填充图像中间可能出现的单个像素点,以防腐蚀后出一个窟窿)
- cardBitmap = JoinImage.dilation(cardBitmap);
- imageView.setImageBitmap(cardBitmap);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("4_dila", cardBitmap);
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- case R.id.toErosBtn: {
- // 腐蚀图像(2次,使得边缘更平滑,减少细化后突出骨架,同时可去除噪点)
- // 图像像素比较低时可能会腐蚀过度。现需要图像宽度至少800,过低像素做好缩放至宽度1000。
- cardBitmap = JoinImage.erosion(cardBitmap, erosionCount);
- imageView.setImageBitmap(cardBitmap);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("5_eros", cardBitmap);
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- case R.id.toThinBtn: {
- // 细化图像(提取图像的骨架特征,方便分析)
- cardBitmap = JoinImage.thinning(cardBitmap, 1);
- imageView.setImageBitmap(cardBitmap);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("6_thin", cardBitmap);
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- case R.id.toSplitBtn: {
- // 分割图像(对细化图像进行分割)
- JoinImage.split(cardBitmap);
- // 获取所有分割图像
- splitBmps = JoinImage.getSplitBmps();
- // 添加GirdView数据
- gridView.setAdapter(new ImageAdapter(this, splitBmps));
- // 隐藏ImageView
- imageView.setVisibility(View.GONE);
- // 显示GirdView
- gridView.setVisibility(View.VISIBLE);
- // 释放缓存图像
- cardBitmap = null;
- // 保存该图像
- if (isSave && null != splitBmps) {
- for (int i = 0; i < splitBmps.length; i++) {
- JoinImage.saveBitmap("split_" + i, splitBmps[i]);
- }
- }
- // 启用下一按钮
- setNextBtnEnabled();
- break;
- }
- }
- }
- /**
- * 图像修正并设置腐蚀次数
- */
- private Bitmap correctBitmap(Bitmap mBitmap) {
- int w = mBitmap.getWidth(), h = mBitmap.getHeight(); // 获取图像长宽
- if (w >= 1500) {
- erosionCount = 2; // 腐蚀次数置2
- int newW = 1000; // 缩放图像至宽度1000
- int newH = h * newW / w; // 高度等比例变化
- // 缩放图像
- Bitmap newBitmap = JoinImage.stretch(mBitmap, newW, newH);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("0_stre", newBitmap);
- }
- return newBitmap;
- } else if (w >= 1000) {
- erosionCount = 2; // 腐蚀次数置2
- return mBitmap;
- } else if (w <= 800) {
- erosionCount = 2; // 腐蚀次数置2
- if (w <= 400) {
- Log.i("图像修正", "图像像素过低!");
- }
- int newW = 1000; // 缩放图像至宽度1000
- int newH = h * newW / w; // 高度等比例变化
- // 缩放图像
- Bitmap newBitmap = JoinImage.stretch(mBitmap, newW, newH);
- // 保存该图像
- if (isSave) {
- JoinImage.saveBitmap("0_stre", newBitmap);
- }
- return newBitmap;
- } else {
- erosionCount = 1; // 腐蚀次数置1
- return mBitmap;
- }
- }
- /**
- * GridView点击事件
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position,
- long id) {
- Toast.makeText(this, "->" + JoinImage.analyseImg(splitBmps[position]),
- Toast.LENGTH_SHORT).show();
- }
- ......
- }
3
)截图
4
)后记
不足之处,多多担待^^。
附件:http://down.51cto.com/data/2360515
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/856750,如需转载请自行联系原作者