Android SurfaceView onTouchEvent配合OpenCV显示

点击蓝字关注我们

以前我们整体的介绍了利用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-

Vaccae的往期经典

OpenCV

《C++ OpenCV案例实战---卡号获取

《C++ OpenCV案例实战---卡片截取(附代码)

《C++ OpenCV透视变换---切换手机正面图片》

《C++ OpenCV实战---获取数量

《C++ OpenCV实战---利用颜色分割获取数量》

《OpenCV4Android NDK方式进行Canny边缘检测》

《OpenCV4Android NDK方式TesserartOCR实时进行识别》

《OpenCV4Android NDK级联方式实时进行人脸检测》

OpenCV4Android NDK稠密光流调用

OpenCV4Android NDK背景消除建模(新Demo附源码)

Android

《Android利用SurfaceView结合科大讯飞修改语音实别UI

《Android关于语音识别的功能实现分析(一)---结构化思维》

《Android关于语音识别的功能实现分析(二)---语义解析》

《Android根据类生成签名字符串

《Android碎片化布局fragment的实战应用

《Android中RecyclerView嵌套RecyclerView

《Android里用AsyncTask后的接口回调

.Net C#

《C#自定义特性(Attribute)讲解与实际应用

《C#根据类生成签名字符串(附DEMO下载地址)

《C++创建动态库C#调用》

《C#与三菱PLC(型号FX2N)串口通讯类

C#开源跨平台机器学习框架ML.NET----二元分类情绪分析

C#开源跨平台机器学习框架ML.NET----结合SqlSugar进行多类分类

数据库及其它

《Oracel存储过程写报表实战》

《Delphi轮播视频和图片程序(用于双屏显示程序)

《SQL随机增加销售数据的脚本编写(附脚本下载地址)

SQL Server中With As的介绍与应用(三)--递归的实战应用

《Oracle通过ODBC连接SQL Server数据库

Oracle利用row_number()over()方式解决插入数据时重复键的问题

 

请扫码

给个关注

微卡智享

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在AndroidSurfaceView显示MJPEG视频流解码,你可以按照以下步骤进行操作: 1. 首先,在你的Android项目中添加必要的依赖项,如OpenCV或其他MJPEG解码库。 2. 创建一个自定义的SurfaceView类,并在其中实现SurfaceHolder.Callback接口。这将允许你监听SurfaceView的生命周期事件。 ```java public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { private MJPEGDecoder mjpegDecoder; public MySurfaceView(Context context) { super(context); getHolder().addCallback(this); } @Override public void surfaceCreated(SurfaceHolder holder) { // 在Surface创建时初始化解码器 mjpegDecoder = new MJPEGDecoder(holder.getSurface()); mjpegDecoder.startDecoding(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 在Surface尺寸发生变化时更新解码器 mjpegDecoder.updateSurface(holder.getSurface()); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 在Surface销毁时停止解码器 mjpegDecoder.stopDecoding(); } } ``` 3. 创建一个MJPEGDecoder类,该类负责接收MJPEG视频流数据并进行解码。你可以使用OpenCV或其他MJPEG解码库来实现这个类。 ```java public class MJPEGDecoder { private Surface surface; private boolean isDecoding; public MJPEGDecoder(Surface surface) { this.surface = surface; isDecoding = true; } public void startDecoding() { // 开始解码线程 new Thread(new Runnable() { @Override public void run() { while (isDecoding) { // 从MJPEG视频流中获取数据并解码 byte[] frameData = getFrameDataFromStream(); // 使用解码库进行解码 Mat frame = decodeMJPEG(frameData); // 将解码后的帧绘制到Surface上 drawFrameToSurface(frame); } } }).start(); } public void updateSurface(Surface surface) { this.surface = surface; } public void stopDecoding() { isDecoding = false; } private byte[] getFrameDataFromStream() { // 从网络或其他来源获取MJPEG视频流数据 // 返回帧的字节数组 } private Mat decodeMJPEG(byte[] frameData) { // 使用OpenCV或其他MJPEG解码库解码帧数据 // 返回解码后的帧(Mat对象) } private void drawFrameToSurface(Mat frame) { Canvas canvas = surface.lockCanvas(null); // 在Surface上绘制解码后的帧 // 可以使用Canvas.drawBitmap()等方法 surface.unlockCanvasAndPost(canvas); } } ``` 4. 在你的Activity中使用自定义的SurfaceView显示MJPEG视频流。 ```java public class MainActivity extends AppCompatActivity { private MySurfaceView surfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surface_view); } } ``` 确保在布局文件中添加一个SurfaceView元素: ```xml <com.example.MySurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 这样,你就可以在AndroidSurfaceView显示解码后的MJPEG视频流了。记得根据你使用的MJPEG解码库进行必要的调整和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值