【Android】OpenCV4Android开发技巧
阅读原文时间:2021年04月20日阅读:1

Android开发技巧

1、根据res路径来读取OpenCV的xml人脸检测文件

private CascadeClassifier cascadeClassifier;

//加载人脸检测xml try { // Copy the resource into a temp file so OpenCV can load it InputStream is = getResources().openRawResource(R.raw.haarcascade_frontalface_alt); File cascadeDir = getDir("cascade", Context.MODE_PRIVATE); File mCascadeFile = new File(cascadeDir, "haarcascade_frontalface_alt.xml"); FileOutputStream os = new FileOutputStream(mCascadeFile);

    byte[] buffer = new byte[4096];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        os.write(buffer, 0, bytesRead); }
    is.close(); os.close(); // Load the cascade classifier cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath()); } catch (Exception e) {
    Log.e("OpenCVActivity", "Error loading cascade", e); }

2、菜单操作Menu

res-menu建菜单menu_main.xml


           

onOptionsItemSelected()方法内实现

@Override
public boolean onOptionsItemSelected(MenuItem item){ switch(item.getItemId()){ case R.id.add_item:
            Toast.makeText(this, "You Clicked Add!!", Toast.LENGTH_SHORT).show();
            break;
        case R.id.remove_item:
            Toast.makeText(this, "You Clicked Remove!", Toast.LENGTH_SHORT).show();
            break;
        case R.id.delete_item:
            Toast.makeText(this, "You Clicked Delete!", Toast.LENGTH_SHORT).show();
            break;
        default: break; } return true;
}

//创建菜单

public boolean onCreateOptionsMenu(Menu menu){
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

3、Button按钮控件/子Activity开启

方式一:

findViewById(R.id.btn_online_demo).setOnClickListener(MainActivity.this);
findViewById(R.id.btn_offline_demo).setOnClickListener(MainActivity.this);

@Override
public void onClick(View v) {
   Intent intent = null;
   switch (v.getId()) { case R.id.btn_online_demo:
      intent = new Intent(MainActivity.this, OnlineFaceDemo.class); startActivity(intent);
      break;
   case R.id.btn_offline_demo:
      intent = new Intent(MainActivity.this, OfflineFaceDemo.class); startActivity(intent);
      break;
   default: break; }
}

方式二:

buttonFirst = (Button)findViewById(R.id.button1);
buttonFirst.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {
        Toast.makeText(MainActivity.this, "You Clicked Button1!", Toast.LENGTH_SHORT).show(); Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); startActivityForResult(photoPickerIntent, PICTURE_CHOOSE); }
});

4、

private File mPictureFile;

……

// 设置相机拍照后照片保存路径
mPictureFile = new File(Environment.getExternalStorageDirectory(), "picture" + System.currentTimeMillis()/1000 + ".jpg");
// 启动拍照,并保存到临时文件
Intent mIntent = new Intent();
mIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
mIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mPictureFile));
mIntent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
startActivityForResult(mIntent, FaceUtil.REQUEST_CAMERA_IMAGE);

5、在Bitmap上绘制区域Canvas(科大讯飞)

private void drawFaceRects(FaceRect[] faces) {
   Paint paint = new Paint(); paint.setColor(Color.YELLOW); paint.setStrokeWidth(Math.max(mImage.getWidth(), mImage.getHeight()) / 100f); paint.setStyle(Style.STROKE); Bitmap bitmap = Bitmap.createBitmap(mImage.getWidth(), mImage.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(mImage, new Matrix(), null);
  
   for (FaceRect face: faces) {
      canvas.drawRect(face.bound, paint);
     
      if (null != face.point) { for (Point p: face.point) {
            canvas.drawPoint(p.x, p.y, paint); }
      }
   }
  
   ((ImageView) findViewById(R.id.offline_img)).setImageBitmap(bitmap);
}

6、保存Bitmap图片到指定路径

//保存图片
public static void saveBitmap(Bitmap bm, String picName) {
   File f = new File("/storage/emulated/0/Pictures/", picName);
   if (f.exists()) {
      f.delete(); } try {
      FileOutputStream out = new FileOutputStream(f); bm.compress(Bitmap.CompressFormat.PNG, 90, out); out.flush(); out.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
}

7、随机生成指定长度的字符串

//length用户要求产生字符串的长度
public static String getRandomString(int length){
   String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random=new Random(); StringBuffer sb=new StringBuffer();
   for(int i=0;i<length;i++){ int number=random.nextInt(62); sb.append(str.charAt(number)); } return sb.toString();
}

8、进度对话框的使用

// 进度对话框
private ProgressDialog mProDialog;

mProDialog = new ProgressDialog(this);
mProDialog.setCancelable(true);
mProDialog.setTitle("请稍后");

mProDialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { // cancel进度框时,取消正在进行的操作
      if (null != mFaceRequest) { mFaceRequest.cancel(); }
   }
});

mProDialog.setMessage("注册中…");
mProDialog.show();

if (null != mProDialog) { mProDialog.dismiss();
}

9、RadioGroup单选按钮控件

RadioGroup alignGruop = (RadioGroup) findViewById(R.id.align_mode);
alignGruop.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup arg0, int arg1) { switch (arg1) { case R.id.detect: isAlign = 0;
                break;
            case R.id.align: isAlign = 1;
                break;
            default: break; }
    }
});

10、Camera操作

private Camera mCamera;
private int mCameraId = CameraInfo.CAMERA_FACING_FRONT;
// Camera nv21格式预览帧的尺寸,默认设置640*480
private int PREVIEW_WIDTH = 640;
private int PREVIEW_HEIGHT = 480;
// 预览帧数据存储数组和缓存数组
private byte[] nv21;
private byte[] buffer;
// 缩放矩阵
private Matrix mScaleMatrix = new Matrix();
// 加速度感应器,用于获取手机的朝向
private Accelerometer mAcc;

nv21 = new byte[PREVIEW_WIDTH * PREVIEW_HEIGHT * 2];
buffer = new byte[PREVIEW_WIDTH * PREVIEW_HEIGHT * 2];
mAcc = new Accelerometer(VideoDemo.this);

@Override
public void onClick(View v) { // 只有一个摄相头,不支持切换 if (Camera.getNumberOfCameras() == 1) {
        showTip("只有后置摄像头,不能切换");
        return; }
    closeCamera();
    if (CameraInfo.CAMERA_FACING_FRONT == mCameraId) { mCameraId = CameraInfo.CAMERA_FACING_BACK; } else { mCameraId = CameraInfo.CAMERA_FACING_FRONT; }
    openCamera();
}

private void openCamera() { if (null != mCamera) { return; } if (!checkCameraPermission()) {
        showTip("摄像头权限未打开,请打开后再试"); mStopTrack = true;
        return; } // 只有一个摄相头,打开后置 if (Camera.getNumberOfCameras() == 1) { mCameraId = CameraInfo.CAMERA_FACING_BACK; } try { mCamera = Camera.open(mCameraId);
        if (CameraInfo.CAMERA_FACING_FRONT == mCameraId) {
            showTip("前置摄像头已开启,点击可切换"); } else {
            showTip("后置摄像头已开启,点击可切换"); }
    } catch (Exception e) {
        e.printStackTrace(); closeCamera();
        return; }

    Parameters params = mCamera.getParameters(); params.setPreviewFormat(ImageFormat.NV21); params.setPreviewSize(PREVIEW_WIDTH, PREVIEW_HEIGHT); mCamera.setParameters(params); // 设置显示的偏转角度,大部分机器是顺时针90度,某些机器需要按情况设置
    mCamera.setDisplayOrientation(90); mCamera.setPreviewCallback(new PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) {
            System.arraycopy(data, 0, nv21, 0, data.length); }
    });

    try { mCamera.setPreviewDisplay(mPreviewSurface.getHolder()); mCamera.startPreview(); } catch (IOException e) {
        e.printStackTrace(); }
}

private void closeCamera() { if (null != mCamera) { mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; }
}

private boolean checkCameraPermission() { int status = checkPermission(permission.CAMERA, Process.myPid(), Process.myUid());
    if (PackageManager.PERMISSION_GRANTED == status) { return true; } return false;
}

// 长按SurfaceView 500ms后松开,摄相头聚集
mFaceSurface.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastClickTime = System.currentTimeMillis();
                break;
            case MotionEvent.ACTION_UP: if (System.currentTimeMillis() - mLastClickTime > 500) { mCamera.autoFocus(null);
                    return true; } break;

            default: break; } return false; }
});

11、读取指定路径的Bitmap

Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.img_01);

12、double转成String

String str_x = Double.toString(pt.x);
String str_y = Double.toString(pt.y);

13、创建线程

HandlerThreaddetectThread = null;
Handler detectHandler = null;

detectThread = new HandlerThread("detect");
detectThread.start();
detectHandler = new Handler(detectThread.getLooper());

detectHandler.post(new Runnable() { @Override public void run() { final Bitmap bit = getFaceInfoBitmap(faceinfo, img); runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bit); System.gc(); textView.setText("Waiting runOnUiThread …"); }
        }); }
});

14、复制Bitmap

Bitmap tmp;
tmp = oribitmap.copy(Bitmap.Config.ARGB_8888, true);

15、Toast提示功能

Toast.makeText(MainActivity.this, "You Clicked Button1!", Toast.LENGTH_SHORT).show();

16、选择图片,并显示

Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, PICTURE_CHOOSE);

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent);

    if (requestCode == PICTURE_CHOOSE) { if (intent != null) { try { //加载图片并显示 ContentResolver resolver = getContentResolver(); Uri originalUri = intent.getData();//获得图片的uri img = MediaStore.Images.Media.getBitmap(resolver, originalUri); imageView.setImageBitmap(img); //获取图片的路径 Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null); cursor.moveToFirst();
                int idx = cursor.getColumnIndex(ImageColumns.DATA); String fileSrc = cursor.getString(idx); textViewDebug.setText("Picture:" + fileSrc); textView.setText("图片加载成功!"); buttonDetect.setVisibility(View.VISIBLE); }catch (IOException e) { textView.setText("TAG-->Error" + e.toString()); }
        } else { textViewDebug.setText("intent == null"); }
    }
}//onActivityResult()

17、NDK使用方法:http://blog.csdn.net/taily_duan/article/details/52484583

public static native int[] grayProc(int[] pixels, int w, int h);

static {
    System.loadLibrary("gray-process");
}

bmp = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
imageView.setImageBitmap(bmp);

int w = bmp.getWidth();
int h = bmp.getHeight();
int[] pixels = new int[w*h];
bmp.getPixels(pixels, 0, w, 0, 0, w, h);
int[] resultInt = grayProc(pixels, w, h);
Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
imageView.setImageBitmap(resultImg);

18、跳转子页面

Button buttonThree = (Button)findViewById(R.id.button3);
buttonThree.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {
        Toast.makeText(MainActivity.this, "跳转到子页面!", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(MainActivity.this, Main2Activity.class); startActivity(intent); finish(); }
});

19、ListView显示:图片+文字

ListView listView = (ListView) findViewById(R.id.listView1);

Map item1 = new HashMap();
item1.put("image", R.drawable.vae);
item1.put("name", "duanjjwei");

Map item2 = new HashMap();
item2.put("image", R.drawable.smyh);
item2.put("name", "zhuangguangli");

List> data = new ArrayList>();
data.add(item1);
data.add(item2);

SimpleAdapter simpleAdapter = new SimpleAdapter(this, data, R.layout.activity_main_item, new String[] { "image", "name" },
      new int[] { R.id.imageView1, R.id.textView1 });
listView.setAdapter(simpleAdapter);

20、

21、

OpenCV for Android开发技巧

1、实现CvCameraViewListener 需要实现的虚函数,定义BaseLoaderCallback回调函数接口,并实现这个类中的onManagerConnected()方法

private CameraBridgeViewBase mOpenCvCameraView;

private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(MainActivity.this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS:
            {
                Log.i(TAG, "OpenCV loaded successfully"); imageMat=new Mat(); //enable camera mOpenCvCameraView.enableView(); } break;
            default:
            { super.onManagerConnected(status); } break; }
    }
};

2、onResume()中加载OpenCV库(OpenCVLoader);

@Override
public void onResume()
{ super.onResume();
    if (!OpenCVLoader.initDebug()) {
        Log.d("OpenCV", "Internal OpenCV library not found. Using OpenCV Manager for initialization"); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this, mLoaderCallback); } else {
        Log.d("OpenCV", "OpenCV library found inside package. Using it!"); mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); }
}

3、Mat变量创建

Mat mRgba = new Mat(height, width, CvType.CV_8UC4);//创建

mRgba.release();//释放

4、根据res路径来读取OpenCV的xml人脸检测文件

private CascadeClassifier cascadeClassifier;

//加载人脸检测xml try { // Copy the resource into a temp file so OpenCV can load it InputStream is = getResources().openRawResource(R.raw.haarcascade_frontalface_alt); File cascadeDir = getDir("cascade", Context.MODE_PRIVATE); File mCascadeFile = new File(cascadeDir, "haarcascade_frontalface_alt.xml"); FileOutputStream os = new FileOutputStream(mCascadeFile);

    byte[] buffer = new byte[4096];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        os.write(buffer, 0, bytesRead); }
    is.close(); os.close(); // Load the cascade classifier cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath()); } catch (Exception e) {
    Log.e("OpenCVActivity", "Error loading cascade", e); }

5、人脸检测

private CascadeClassifier cascadeClassifier;

//检测并显示 MatOfRect faces = new MatOfRect();
    if (cascadeClassifier != null) { cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 2, 2, new Size(mGray.height()/5, mGray.height()/5), new Size()); }

    Rect[] facesArray = faces.toArray();
    for (int i = 0; i <facesArray.length; i++) {
        Core.rectangle(mShow, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3); Point pt = new Point(); pt.x = facesArray[i].br().x; pt.y = facesArray[i].br().y; String str_x = Double.toString(pt.x); String str_y = Double.toString(pt.y); String str = str_x + ", " + str_y; Core.putText(mShow,str, pt, Core.FONT_HERSHEY_SIMPLEX, 1, new Scalar(255,23,0),2); }

6、Bitmap与Mat转换/OpenCV彩色转灰度

//---------------------OpenCV处理------------------------
Mat mat_src = new Mat(bmp.getWidth(), bmp.getHeight(), CvType.CV_8UC4);
Utils.bitmapToMat(bmp, mat_src);
Mat mat_gray = new Mat(bmp.getWidth(), bmp.getHeight(), CvType.CV_8UC1);
Imgproc.cvtColor(mat_src, mat_gray, Imgproc.COLOR_BGRA2GRAY, 1);
Bitmap bmp_dst = Bitmap.createBitmap(mat_gray.cols(), mat_gray.rows(), Bitmap.Config.ARGB_8888 );
Utils.matToBitmap(mat_gray, bmp_dst);

7、图片的缩放/或使用resize

//缩放图片大小
public static Bitmap getScaledBitmap(Bitmap m_img, float sx, float sy) {
   Matrix matrix = new Matrix(); matrix.postScale(sx, sy); Bitmap rst = Bitmap.createBitmap(m_img, 0, 0, m_img.getWidth(), m_img.getHeight(), matrix, true);
   return rst;
}

8、Mat中截取Rect部分的图片

Rect[] facesArray = faces.toArray();

Mat copyMat = mGray.submat(facesArray[i]);

9、使用Core.putText在图像上输入文字

String str = str_x + ", " + str_y + " S1:" + c1 + " S2:"+ c2;
Core.putText(mat_src,str, pt, Core.FONT_HERSHEY_SIMPLEX, 1, new Scalar(255,23,0),2);

10、Mat、Bitmap、IplImage相互转换

//Mat转成Bitmap
public Bitmap MatToBitmap(Mat src) {
    Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(src, bitmap);
    return bitmap;
}

//Bitmap转换成Mat
public Mat bitmapToMat(Bitmap bitmap) {
    Mat rgbMat = new Mat(); Utils.bitmapToMat(bitmap, rgbMat);
    return rgbMat;
}

//IplImage转换成Bitmap
public Bitmap IplImageToBitmap(opencv_core.IplImage iplImage) {
    Bitmap bitmap = null; bitmap = Bitmap.createBitmap(iplImage.width(), iplImage.height(), Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(iplImage.getByteBuffer());
    return bitmap;
}

//Bitmap转换成IplImage
public opencv_core.IplImage bitmapToIplImage(Bitmap bitmap) {
    opencv_core.IplImage iplImage; iplImage = opencv_core.IplImage.create(bitmap.getWidth(), bitmap.getHeight(), opencv_core.IPL_DEPTH_8U, 4); bitmap.copyPixelsToBuffer(iplImage.getByteBuffer());
    return iplImage;
}

11、图像的灰度直方图

public double CmpPic(opencv_core.IplImage Image1, opencv_core.IplImage Image2) { int l_bins = 20;
    int hist_size[] = {l_bins};
    float v_ranges[] = {0, 100};
    float ranges[][] = {v_ranges}; opencv_core.IplImage imageArr1[] = {Image1}; opencv_core.IplImage imageArr2[] = {Image2}; opencv_imgproc.CvHistogram Histogram1 = opencv_imgproc.CvHistogram.create(1, hist_size, opencv_imgproc.CV_HIST_ARRAY, ranges, 1); opencv_imgproc.CvHistogram Histogram2 = opencv_imgproc.CvHistogram.create(1, hist_size, opencv_imgproc.CV_HIST_ARRAY, ranges, 1); cvCalcHist(imageArr1, Histogram1, 0, null); cvCalcHist(imageArr2, Histogram2, 0, null); cvNormalizeHist(Histogram1, 100.0); cvNormalizeHist(Histogram2, 100.0);
    return cvCompareHist(Histogram1, Histogram2, opencv_imgproc.CV_COMP_CORREL);
}

12、RGB转换成HSV

int nHeight = bitmap.getHeight()/10;
int nWidth  = bitmap.getWidth()/10;
Mat mat_small = new Mat(nHeight, nWidth, CvType.CV_8UC4);
Imgproc.resize(mat_src, mat_small, new Size(nWidth, nHeight));
Mat mat_gray = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC1);
Imgproc.cvtColor(mat_small, mat_gray, Imgproc.COLOR_BGRA2GRAY, 1);

//-------------------------------------------------------------------
Mat frame = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC4);
mat_small.copyTo(frame);

Mat frameHSV = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC3);; // hsv空间
Mat mask = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC1);
Mat dst = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC3); // 输出图像
//dst.copyTo(frame);
// 中值滤波,去除椒盐噪声
Imgproc.medianBlur(frame, frame, 5);
Imgproc.cvtColor(frame, frameHSV, Imgproc.COLOR_RGB2HSV, 3);
Mat dstTemp1 = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC1);
Mat dstTemp2 = new Mat(mat_small.cols(), mat_small.rows(), CvType.CV_8UC1);
// 对HSV空间进行量化,得到二值图像,亮的部分为手的形状
Core.inRange(frameHSV, new Scalar(0, 30, 30), new Scalar(40, 170, 256), dstTemp1);
Core.inRange(frameHSV, new Scalar(156, 30, 30), new Scalar(180, 170, 256), dstTemp2);
Core.bitwise_or(dstTemp1, dstTemp2, mask);
//dstTemp1.copyTo(mask);
// 形态学操作,去除噪声,并使手的边界更加清晰
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
Imgproc.erode(mask, mask, element);
Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_OPEN, element);
Imgproc.dilate(mask, mask, element);
Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_CLOSE, element);

frame.copyTo(dst, mask);

13、

14、

15、

16、

17、

18、

19、