首先在官网下载ADT和OpenCV for Android。
在使用OpenCV的时候是将其作为一个库,供你的App使用。所以先将下载好的OpenCV Libiary-2.4.9作为project导入ADT。然后将OpenCV Libiary-2.4.9设为库:右键属性,选择左侧的Android,选择右侧的Ls Library
之后新建一个project,命名为OpenCVDemo,右键属性,选择Android,选择左侧的Add,添加刚才设为库的OpenCV Libiary-2.4.9,确定。现在就可以使用OpenCV了。
另外,你需要安装OpenCV Manager
在OpenCVDemo中,需要对OpenCV进行Init,代码如下:
//加载OpenCVLoader
static {
if (!OpenCVLoader.initDebug()) {
android.util.Log.e("opencv initialization", "error");
}
}
在OpenCVDemo中,我们以两种途径(本地图片、相机)获取图片后,对图像进行一些简单地处理,效果如图
接下来,看具体实现。可以大致分为4部分:
1.ActionBar点击事件对获取图片的途径进行选择(相册和相机),将图片放入ImageView
2.定义按钮的点击事件,对图片进行相应的处理
3.建一个类,将处理图片的方法封装进去
4.由于“soble导数”这个方法处理的较慢,所以建一个异步线程Async Task,并在处理的时候用环状processbar提示等待。
MainActivity extends ActionBarActivity需要用到库android-support-v7-appcompat,方法和引用OpenCV库的方法是一样的。
然后需要implements ActionBarActivity中的抽象方法(actionbar的选择项位于res-menu-main.xml)
//anctionBar的点击事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
//使用相册
case R.id.usePictureFormGallery:
Intent getAlbumIntent = new Intent(Intent.ACTION_GET_CONTENT);
getAlbumIntent.setType(IMAGE_TYPE);
startActivityForResult(getAlbumIntent, IMAGE_CODE);
return true;
//使用相机
case R.id.takePicture:
//生成父目录,没有则创建
File dir = new File(Environment.getExternalStorageDirectory(),"MyPic");
if (!dir.exists()){
dir.mkdirs();
}
//生成图片
mCamarePicFile = new File(dir, System.currentTimeMillis()+".jpg");
if(!mCamarePicFile.exists()){
try {
mCamarePicFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//在Intent中添加拍摄图片的uri
Intent getCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
getCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(mCamarePicFile));
startActivityForResult(getCameraIntent,CAMERA_CODE);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
ContentResolver resolverContentResolver = getContentResolver();
//得到拍照的图片的Uri:Uri.fromFile(“创建的File的引用”);
if(requestCode ==CAMERA_CODE){
Uri uri = Uri.fromFile(mCamarePicFile);
try {
mOriginAtImageViewBitmap = MediaStore.Images.Media.getBitmap(
resolverContentResolver, uri);
mShowImgImageView.setImageBitmap(mOriginAtImageViewBitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//得到相册中选中图片的Uri:data.getData()
if (requestCode == IMAGE_CODE) {
try {
Uri uri = data.getData();// 图片uri
mOriginAtImageViewBitmap = MediaStore.Images.Media.getBitmap(
resolverContentResolver, uri);
mShowImgImageView.setImageBitmap(mOriginAtImageViewBitmap);
} catch (Exception e) {
Log.e("TAG-->Error", e.toString());
}
}
}
接下来是处理图像的按钮点击事件,
public class MainActivity extends ActionBarActivity implements OnClickListener {
public void initUI() {
mShowImgImageView = (ImageView) findViewById(R.id.imgShow);
mGrayButton = (Button) findViewById(R.id.gray);
mGrayButton.setOnClickListener(this);
mBackToOriginButton = (Button) findViewById(R.id.backToOrigin);
mBackToOriginButton.setOnClickListener(this);
mErodeButton = (Button) findViewById(R.id.erode);
mErodeButton.setOnClickListener(this);
mdilateButton = (Button) findViewById(R.id.dilate);
mdilateButton.setOnClickListener(this);
mSobelButton = (Button) findViewById(R.id.sobel);
mSobelButton.setOnClickListener(this);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mProgressBar.setOnClickListener(this);
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
}
//点击事件的处理
@Override
public void onClick(View v) {
ImageHandle chioceImageHandle = new ImageHandle();
switch (v.getId()) {
case R.id.backToOrigin:
mResultAtImageViewBitmap = mOriginAtImageViewBitmap;
break;
case R.id.gray:
mResultAtImageViewBitmap = chioceImageHandle
.gray(mOriginAtImageViewBitmap);
break;
case R.id.erode:
// 可以连续腐蚀,所以应从当前的imageView中取bitmap,而不是original的bitmap;
mShowImgImageView.setDrawingCacheEnabled(true);
Bitmap erodeBitmap = mShowImgImageView.getDrawingCache();
mResultAtImageViewBitmap = chioceImageHandle.erode(erodeBitmap);
mShowImgImageView.setDrawingCacheEnabled(false);
break;
case R.id.dilate:
mShowImgImageView.setDrawingCacheEnabled(true);
Bitmap dilateBitmap = mShowImgImageView.getDrawingCache();
mResultAtImageViewBitmap = chioceImageHandle.erode(dilateBitmap);
mShowImgImageView.setDrawingCacheEnabled(false);
break;
case R.id.sobel:
// 启用asyncTask
//注意:此处调用线程,应该先赋予mResultAtImageViewBitmap,使其及时显示在imageview,要不然在线程执行的时候imageview中显示的是原来的图片
mResultAtImageViewBitmap = mOriginAtImageViewBitmap;
new SobelImageTask().execute(mOriginAtImageViewBitmap);
break;
default:
break;
}
mShowImgImageView.setImageBitmap(mResultAtImageViewBitmap);
}
下面是ImageHandle类:
//图像处理类 封装方法
class ImageHandle {
// 尽量复用Mat
private Mat srcMat = new Mat();
private Mat dstMat = new Mat();
public Bitmap gray(Bitmap srcBitmap) {
Utils.bitmapToMat(srcBitmap, srcMat);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
srcBitmap.getHeight(), Config.RGB_565);
Imgproc.cvtColor(srcMat, dstMat, Imgproc.COLOR_RGB2GRAY);
Utils.matToBitmap(dstMat, dstBitmap);
return dstBitmap;
}
public Bitmap erode(Bitmap srcBitmap) {
Utils.bitmapToMat(srcBitmap, srcMat);
int erosion_size = 5;
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ERODE,
new Size(2 * erosion_size + 1, 2 * erosion_size + 1));
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
srcBitmap.getHeight(), Config.RGB_565);
Imgproc.erode(srcMat, dstMat, kernel);
Utils.matToBitmap(dstMat, dstBitmap);
return dstBitmap;
}
// 改动了dilate,效果和erode差不多?
public Bitmap dilate(Bitmap srcBitmap) {
Utils.bitmapToMat(srcBitmap, srcMat);
int erosion_size = 2;
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,
new Size(1 * erosion_size + 1, 1 * erosion_size + 1));
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
srcBitmap.getHeight(), Config.RGB_565);
Imgproc.dilate(srcMat, dstMat, kernel);
Utils.matToBitmap(dstMat, dstBitmap);
return dstBitmap;
}
public Bitmap sobel(Bitmap srcBitmap) {
//显示progressbar
// mProgressBar.setVisibility(ProgressBar.VISIBLE);
int ddepth = -1;
// 降噪
Utils.bitmapToMat(srcBitmap, srcMat);
Imgproc.GaussianBlur(srcMat, srcMat, new Size(3, 3), 0, 0,
Imgproc.BORDER_DEFAULT);
Utils.matToBitmap(srcMat, srcBitmap);
// 灰化
Bitmap grayBitmap = gray(srcBitmap);
Mat gray_x = new Mat();
Mat gray_y = new Mat();
Mat gray_x_y = new Mat();
Utils.bitmapToMat(grayBitmap, srcMat);// srcMat is gray now
// x轴求导
Imgproc.Sobel(srcMat, gray_x, ddepth, 1, 0);
// y轴求导
Imgproc.Sobel(srcMat, gray_y, ddepth, 0, 1);
Core.addWeighted(gray_x, 0.5, gray_y, 0.5, 0.0, gray_x_y);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
srcBitmap.getHeight(), Config.RGB_565);
Utils.matToBitmap(gray_x_y, dstBitmap);
return dstBitmap;
}
}
有几个常用的转化应记住
从ImageView到Bitmap:
mShowImgImageView.setDrawingCacheEnabled(true);
Bitmap erodeBitmap = mShowImgImageView.getDrawingCache();
mShowImgImageView.setDrawingCacheEnabled(false);
从Bitmap到Mat:
Utils.bitmapToMat(srcBitmap, srcMat);
从Mat到Bitmap:
Utils.matToBitmap(gray_x_y, dstBitmap);
//异步类,用于sobel倒数的处理
private class SobelImageTask extends AsyncTask {
@Override
protected Bitmap doInBackground(Bitmap... params) {//传递的是数组,所以取值的时候是“params[]”
publishProgress("请稍等","ss");//可以添加一个textview,放入“请稍等”
// 开始加载图片,显示progressbar(wrong!!)!!!!!不可以在非UI线程里 联系UI!!!!!
return new ImageHandle().sobel(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mShowImgImageView.setImageBitmap(bitmap);
// 图片加载完成,去掉progressbar
mProgressBar.setVisibility(ProgressBar.GONE);
}
@Override
protected void onProgressUpdate(String... values) {
mProgressBar.setVisibility(ProgressBar.VISIBLE);
}
}
AsyncTask
三个参数
是以下方法的对应类型,
分别是:
Bitmap doInBackground(Bitmap... params)//返回值传入onPostExecute(Bitmap bitmap)
onProgressUpdate(String... values)//传入值为publishProgress("请稍等","ss")的参数———这个数组
onPostExecute(Bitmap bitmap) //doInBackGround的返回值
调用异步线程的时候,只需要
new SobelImageTask().execute(mOriginAtImageViewBitmap);
需要注意的是,凡是涉及与UI的交互,都需要放入onXX方法内,例如processbar的显示问题。