Android SurfaceView onTouchEvent配合OpenCV显示

  • 2019 年 10 月 7 日
  • 筆記

以前我们整体的介绍了利用SurfaceView调用系统的Camera显示图像,并且通过NDK OpenCV的方式进行图像处理,今天这篇我们就是来介绍一下,在SurfaceView中点击显示图像中的区域进行定位,方便我们手动调整图像的。最后完整的代码会在整个系列都做完后上传到GItHub中。

视频演示

视频说明

通过SurfaceView中点击事件其实相对来说很简单,只要重写onTouchEvent事件就可以。

在参数event里面的getRawX和getRawY就可以获取到点击的坐标点。但是看过以前SurfaceView调用camera的朋友应该记得,我们还除了要旋转相机角度,还要对画布的大小对显示的图像进行缩放,所以本章的重点是解决我们点击的图像怎么对应到上面视频中显示出来的红点位置。

实现思路

点击时进行计算处理

  1. 在onTouchevent事件中获取到屏幕的宽和高。
  2. 通到getRawx和getRawY的坐标计算出在总屏幕中位置比例。
  3. 在调用NDK时通过用生成的图片的宽高乘比例计算出点击的位置坐标(会有一点小的误差,但不影响)。
  4. NDK实现中对坐标进行画圈显示出来。

代码实现

程序框架我们就不在重新搭建了,用的还是《Android利用SurfaceView显示Camera图像爬坑记(六) — 用OpenCV进行Canny边缘检测》那个Demo。

接下来直接开始

01

VaccaeSurfaceView修改

首先我们先定义几个变量,用于计算我们的坐标点,如图:

然后在VaccaeSurfaceView中直接重写onTouchEvent事件,如下:

我们先通过定义DisplayMetrics来获取到屏幕的分辨率,然后要根据点击的位置横坐标除屏幕长度,纵坐标除屏幕高度计算出对应的比例。

然后在原来的图片处理方法nv21ToBitmap 中,根据图像大小重新计算图片中的坐标x,y的点

nv21ToBitmap方法:

private Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {      Bitmap bitmap=null;      try {          YuvImage image=new YuvImage(nv21, ImageFormat.NV21, width, height, null);          ByteArrayOutputStream stream=new ByteArrayOutputStream();          image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);          //将rawImage转换成bitmap          BitmapFactory.Options options=new BitmapFactory.Options();          options.inPreferredConfig=Bitmap.Config.ARGB_8888;          bitmap=BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size(), options);            //加入图像旋转          Matrix m=new Matrix();          m.postRotate(rotatedegree);          bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),                  m, true);            //调用JNI方法处理图像          if (istouch) {              touchx=(int) (bitmap.getWidth() * touchxscale);              touchy=(int) (bitmap.getHeight() * touchyscale);                bitmap=VaccaeOpenCVJNI.getCameraframetouchbitbmp(bitmap, touchx, touchy);          }          stream.close();      } catch (IOException e) {          e.printStackTrace();      }      return bitmap;  }

02

JNI方法实现

我们在VaccaeOpenCVJNI中加入一个新的native方法

然后通过ALT+Enter直接在我们的native-lib.cpp里面自动生成对应的方法,主要就是生成了Mat图像后加入刚才的点坐标进行画半径50的圆并填充。

完整的实现代码

extern "C"  JNIEXPORT jobject JNICALL  Java_dem_vac_surfaceviewdemo_VaccaeOpenCVJNI_getCameraframetouchbitbmp(JNIEnv *env, jclass clazz,                                                                         jobject bmp, jint touchx,                                                                         jint touchy) {      AndroidBitmapInfo bitmapInfo;      void *pixelscolor;      int ret;        //获取图像信息,如果返回值小于0就是执行失败      if ((ret = AndroidBitmap_getInfo(env, bmp, &bitmapInfo)) < 0) {          LOGI("AndroidBitmap_getInfo failed! error-%d", ret);          return NULL;      }        //判断图像类型是不是RGBA_8888类型      if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {          LOGI("BitmapInfoFormat error");          return NULL;      }        //获取图像像素值      if ((ret = AndroidBitmap_lockPixels(env, bmp, &pixelscolor)) < 0) {          LOGI("AndroidBitmap_lockPixels() failed ! error=%d", ret);          return NULL;      }        //生成源图像      cv::Mat src(bitmapInfo.height, bitmapInfo.width, CV_8UC4, pixelscolor);        //点击的点      cv::Point point(touchx, touchy);      cv::circle(src, point, 50, cv::Scalar(255, 0, 0), -1);        //获取原图片的参数      jclass java_bitmap_class = (jclass) env->FindClass("android/graphics/Bitmap");      jmethodID mid = env->GetMethodID(java_bitmap_class, "getConfig",                                       "()Landroid/graphics/Bitmap$Config;");      jobject bitmap_config = env->CallObjectMethod(bmp, mid);      //将SRC转换为图片      jobject _bitmap = mat2bitmap(env, src, false, bitmap_config);        AndroidBitmap_unlockPixels(env, bmp);          return _bitmap;  }  

这样我们的SurfaceView中点击效果在OpenCV中就实现了,下图就是视频中的点击效果显示。

-END-