您的位置:首页 > 移动开发 > Android开发

Android图像处理简单例子 推荐

2012-05-08 10:11 645 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jni 图像 处理 android