前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android SurfaceView onTouchEvent进阶操作OpenCV显示

Android SurfaceView onTouchEvent进阶操作OpenCV显示

作者头像
Vaccae
发布2019-09-27 17:05:21
1.5K0
发布2019-09-27 17:05:21
举报
文章被收录于专栏:微卡智享微卡智享微卡智享

前一篇文章《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-

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档