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-