OpenCV+ADT 第一个基于Android的OpenCV Demo

首先在官网下载ADT和OpenCV for Android。

在使用OpenCV的时候是将其作为一个库,供你的App使用。所以先将下载好的OpenCV Libiary-2.4.9作为project导入ADT。然后将OpenCV Libiary-2.4.9设为库:右键属性,选择左侧的Android,选择右侧的Ls LibraryOpenCV+ADT 第一个基于Android的OpenCV Demo_第1张图片

之后新建一个project,命名为OpenCVDemo,右键属性,选择Android,选择左侧的Add,添加刚才设为库的OpenCV Libiary-2.4.9,确定。现在就可以使用OpenCV了。

OpenCV+ADT 第一个基于Android的OpenCV Demo_第2张图片

另外,你需要安装OpenCV Manager

OpenCV+ADT 第一个基于Android的OpenCV Demo_第3张图片

在OpenCVDemo中,需要对OpenCV进行Init,代码如下:

//加载OpenCVLoader
	static {
		if (!OpenCVLoader.initDebug()) {
			android.util.Log.e("opencv initialization", "error");
		}
	}
在OpenCVDemo中,我们以两种途径(本地图片、相机)获取图片后,对图像进行一些简单地处理,效果如图

OpenCV+ADT 第一个基于Android的OpenCV Demo_第4张图片

接下来,看具体实现。可以大致分为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);
		}
	}

接下来处理和startactivity相对应的方法 onActivityResult
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);
	}

继承onClickListener接口后,对控件初始化,然后重写onClick方法

//点击事件的处理 
	@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);
	}

在onClick方法内用到了自定义的类 ImageHandle其中封装的一些方法,和处理sobel的时候的异步线程。

下面是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的显示问题。
OpenCV+ADT 第一个基于Android的OpenCV Demo_第5张图片 OpenCV+ADT 第一个基于Android的OpenCV Demo_第6张图片









你可能感兴趣的:(OpenCV4Android)