Android SurfaceView onTouchEvent进阶操作OpenCV显示
- 2019 年 10 月 8 日
- 筆記
前一篇文章《Android SurfaceView onTouchEvent配合OpenCV显示》介绍了Android SurfaceView中通过onTouchEvent事件点击后在OpenCV中画了个圆显示出来,本身onTouchEvent还可以有按下,移动,抬起的捕获,所以本篇我们在上一篇的基础上做一下进阶的显示。
实现效果
通过点击,移动在图像上画上矩形
★ 实现思路 ★
在OpenCV中画矩形需要两个坐标点即可,所以我们在点击屏幕时传递给OpenCV一个启始坐标点和一个结束坐标点,OpenCV中对每一帧的图像的传递进来的两个坐标点画矩形即可。细分下来我们的步骤如下:
01 |
手指按下时记录起始坐标和结束坐标相等 |
---|---|
02 |
手指在滑动中更新结束坐标 |
03 |
手指抬起时传递一个标志(这里没写后面的,后面的我们会结合前面学的RecyclerView综合使用) |
01
VaccaeSurfaceView修改
在上章的Demo基础上再加入一对新的点击位置比例,这里只计算位置的比例,在调用OpenCV时重新要甩这个比例来计算坐标点进行传入。
上面为onTouchEvent事件,把手指按下、移动、抬起时的操作都进行了处理,代码如下:
@Override public boolean onTouchEvent(MotionEvent event) { //获取屏幕分辨率 DisplayMetrics metric=new DisplayMetrics(); windowManager.getDefaultDisplay().getMetrics(metric); int width=metric.widthPixels; // 宽度(PX) int height=metric.heightPixels; // 高度(PX) int action = event.getAction(); switch (action){ case MotionEvent.ACTION_DOWN: Log.e("surfaceviewtouch", "onTouch: down"); touchxscale=event.getRawX() / width; touchyscale=event.getRawY() / height; touchmovexscale=touchxscale; touchmoveyscale=touchyscale; istouch=false; break; case MotionEvent.ACTION_UP: Log.e("surfaceviewtouch", "onTouch: up"); touchmovexscale=event.getRawX() / width; touchmoveyscale=event.getRawY() / height; istouch=true; break; case MotionEvent.ACTION_MOVE: Log.e("surfaceviewtouch", "onTouch: move"); touchmovexscale=event.getRawX() / width; touchmoveyscale=event.getRawY() / height; istouch=false; break; } return true; }
上图中调用OpenCV的方法nv21ToBitmap里我们重新计算了起始坐标和结束坐标的位置,然后新写了一个JNI的方法进行调用,代码如下:
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 (!(touchxscale == 0 && touchyscale == 0 && touchmovexscale == 0 && touchmoveyscale == 0)) { List<Point> points=new ArrayList<>(); touchx=(int) (bitmap.getWidth() * touchxscale); touchy=(int) (bitmap.getHeight() * touchyscale); points.add(new Point(touchx, touchy)); touchx=(int) (bitmap.getWidth() * touchmovexscale); touchy=(int) (bitmap.getHeight() * touchmoveyscale); points.add(new Point(touchx, touchy)); bitmap=VaccaeOpenCVJNI.Cameraframetouchgetbitbmp(bitmap, points, istouch); } stream.close(); } catch (IOException e) { e.printStackTrace(); } return bitmap; }
02
VaccaeOpenCVJNI的修改
我们在VaccaeOpenCV的类中再加入一个新的方法Cameraframetouchgetbitbmp,参数为传入的图像,坐标的集合,还有一个是结束标志。
03
native-lib.cpp的修改
在VaccaeOpenCV中的Cameraframetouchgetbitbmp中按ALT+ENTER后会在我们的native-lib.cpp中自动创建了对应的方法。
核心方法
像在OpenCV中画圆,画矩形我们最简单的方法已经会,这里主要就是看看传进来的List<Point>我们怎么取出来,在JNI中传递LIst集合,我们在《Android NDK编程(八)— JNI中List结构的类数据做为参数》中就已经学过,这里正好在实战中应用上了。
完整的Cameraframetouchgetbitbmp方法代码
extern "C" JNIEXPORT jobject JNICALL Java_dem_vac_surfaceviewdemo_VaccaeOpenCVJNI_Cameraframetouchgetbitbmp(JNIEnv *env, jclass clazz, jobject bmp, jobject points, jboolean isovertouch) { 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); //获取ArrayList类引用 jclass list_jcls = env->FindClass("java/util/ArrayList"); if (list_jcls == NULL) { LOGI("ArrayList没找到相关类!"); return bmp; } //获取ArrayList对象的get()的methodID jmethodID list_get = env->GetMethodID(list_jcls, "get", "(I)Ljava/lang/Object;"); //获取ArrayList对象的size()的methodID jmethodID list_size = env->GetMethodID(list_jcls, "size", "()I"); //然后获取我们的Point类的class jclass jcls = env->FindClass("android/graphics/Point"); if (jcls == NULL) { return bmp; } //获取Point的值 jfieldID pointx = env->GetFieldID(jcls, "x", "I"); jfieldID pointy = env->GetFieldID(jcls, "y", "I"); //获取集合的里的个数 int size= env->CallIntMethod(points,list_size); //定义开始和结束的Point cv::Point pointstart; cv::Point pointend; //只取第一个启始点和最后一个结束点 if (size < 1) { return bmp; } else { //获取启始点的Point jobject item = env->CallObjectMethod(points, list_get, 0); pointstart = cv::Point(env->GetIntField(item, pointx), env->GetIntField(item, pointy)); //获取结束点的Point item = env->CallObjectMethod(points, list_get, size - 1); pointend = cv::Point(env->GetIntField(item, pointx), env->GetIntField(item, pointy)); } //画矩形框 cv::rectangle(src, pointstart, pointend, cv::Scalar(255, 0, 0),3); //获取原图片的参数 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; }
下图就是手指按下后,移动中画矩形的图像效果
-END-