当前位置: 首页 > web >正文

【代码解析】opencv 安卓 SDK sample - 1 - HDR image

很久没有写安卓了,复习复习。用的是官方案例,详见opencv-Android-sdk

// 定义包名,表示该类的组织路径
package org.opencv.samples.tutorial1;// 导入所需的OpenCV和Android类库
import org.opencv.android.CameraActivity;  // OpenCV相机活动基类
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;  // 相机帧数据
import org.opencv.android.OpenCVLoader;  // OpenCV加载器
import org.opencv.core.Mat;  // OpenCV矩阵类,用于存储图像数据
import org.opencv.android.CameraBridgeViewBase;  // 相机视图基类
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;  // 相机监听接口
import org.opencv.android.Utils;  // OpenCV工具类
import org.opencv.core.Core;  // OpenCV核心功能
import org.opencv.imgproc.Imgproc;  // 图像处理模块
import org.opencv.photo.Photo;  // 照片处理模块
import org.opencv.core.*;  // OpenCV核心模块// 导入Android相关类库
import android.os.Bundle;  // 用于保存Activity状态
import android.util.Log;  // 日志工具
import android.view.SurfaceView;  // 表面视图
import android.view.WindowManager;  // 窗口管理
import android.widget.Toast;  // 提示消息
import android.widget.Button;  // 按钮控件
import android.view.View;  // 视图基类
import android.os.Environment;  // 环境信息
import android.graphics.Bitmap;  // 位图类// 导入Java工具类
import java.util.Collections;  // 集合工具
import java.util.ArrayList;  // 动态数组
import java.util.List;  // 列表接口
import java.io.File;  // 文件类
import java.io.FileOutputStream;  // 文件输出流
import java.text.SimpleDateFormat;  // 日期格式化
import java.util.Date;  // 日期类

Tutorial1Activity 类

定义

public class Tutorial1Activity extends CameraActivity implements CvCameraViewListener2

继承自 CameraActivity ( 是Activity 基类,它封装了对摄像头的基本处理逻辑,比如打开摄像头、获取帧数据等。)

接口声明

OpenCV 摄像头帧回调接口 CvCameraViewListener2。

implements CvCameraViewListener2

表示这个类 实现了 接口 CvCameraViewListener2,必须实现该接口的全部方法

这个接口是 OpenCV 提供的,用于处理来自摄像头的数据帧。它有几个关键方法:

public interface CvCameraViewListener2 {void onCameraViewStarted(int width, int height);void onCameraViewStopped();Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame);
}
方法作用
onCameraViewStarted()摄像头启动时初始化 Mat、变量等
onCameraViewStopped()摄像头停止时释放资源
onCameraFrame()每帧图像自动调用,可以处理图像

📌 各方法说明:

onCameraViewStarted(int width, int height):摄像头视图开始时调用。

onCameraViewStopped():摄像头视图停止时调用。

onCameraFrame(CvCameraViewFrame inputFrame):每帧图像采集时调用,可以在这里进行图像处理(如边缘检测、滤波等),返回 Mat 类型图像结果用于渲染显示。

构造函数

 // 构造函数public Tutorial1Activity() {// 打印日志,记录实例化信息Log.i(TAG, "Instantiated new " + this.getClass());}

定义

    // 定义日志标签常量定义一个日志标签(log tag),用于在 AndroidLogcat 控制台中打印日志。便于调试、排查错误,推荐每个类定义一个 TAGprivate static final String TAG = "OCVSample::Activity";// 声明相机视图对象声明一个 OpenCV 的摄像头视图控件,它负责打开摄像头、采集图像帧、并把图像送入 onCameraFrame() 回调处理。封装了摄像头的打开、预览、帧捕获、图像格式转换等功能。CameraBridgeViewBaseOpenCV for Android 提供的一个抽象类,常用的子类是 JavaCameraViewprivate CameraBridgeViewBase mOpenCvCameraView;// 声明拍照按钮在 onCreate 中设置利用 setOnClickListener 触发private Button mCaptureButton;// 声明存储RGBA图像的矩阵onCameraFrame() 回调函数中,OpenCV 会将摄像头捕获的帧传递进来private Mat mRgba;// HDR相关声明private Button mHdrButton;  // HDR按钮private boolean mIsCapturingHDR = false;  // HDR拍摄标志位private List<Mat> mExposureSequence = new ArrayList<>();  // 存储不同曝光图像的列表private static final float[] EXPOSURE_VALUES = {0.5f, 1.0f, 2.0f};  // 定义三种曝光值

函数解析

在 Android 中,Activity 有一个完整的生命周期管理,典型调用顺序如下:

onCreate() → 创建视图和变量

onStart() → Activity 对用户可见

onResume() → Activity 可交互(获取焦点)

onPause() → 准备进入后台

onStop() → Activity 不再可见

onDestroy() → Activity 被销毁

Activity 的构造函数 onCreate

系统会先调用隐藏的构造函数,然后调用 onCreate()。 当该 Activity 第一次被创建时(即启动时),系统会自动调用它。到 onCreate() 时,系统已经准备好了界面容器、上下文环境(Context)、资源引用、生命周期管理器。
这时才能用 findViewById方法

在 onCreate() 中,通常执行以下操作:

  1. 设置界面布局
setContentView(R.layout.activity_main);

加载 XML 中定义的 UI 界面。

  1. 初始化 UI 控件和变量
mOpenCvCameraView = findViewById(R.id.java_camera_view);
mCaptureButton = findViewById(R.id.capture_button);

完成变量与界面元素的绑定,准备用户交互。

  1. 设置控件监听器
mCaptureButton.setOnClickListener(v -> captureImage());

为按钮或其他 UI 元件设置回调函数。

  1. 初始化 OpenCV 或其他第三方库
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, mLoaderCallback);

加载 OpenCV 库并处理初始化回调。

// Activity创建时的回调方法
@Override 对父类 Activity 中的 onCreate 方法的重写。
Bundle savedInstanceState:
是一个用于存储界面状态的数据结构。
当 Activity 被意外销毁后(如屏幕旋转、被系统回收等),可以通过它恢复先前的状态。public void onCreate(Bundle savedInstanceState) {// 调用父类onCreate方法super.onCreate(savedInstanceState);// 打印日志Log.i(TAG, "called onCreate");// 初始化OpenCV本地库if (OpenCVLoader.initLocal()) {// 初始化成功日志Log.i(TAG, "OpenCV loaded successfully");} else {// 初始化失败日志Log.e(TAG, "OpenCV initialization failed!");// 显示初始化失败提示(Toast.makeText(this, "OpenCV initialization failed!", Toast.LENGTH_LONG)).show();return;}// 设置窗口标志,保持屏幕常亮getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);// 设置布局文件setContentView(R.layout.tutorial1_surface_view);// 获取相机视图引用XML 布局文件中声明的 OpenCV 摄像头预览控件绑定到 Java 代码中的变量 mOpenCvCameraView 上,以便后续调用方法控制摄像头。
强制类型转换,因为 findViewById() 返回的是通用类型 View,而你希望将它当作 CameraBridgeViewBase 来使用。
CameraBridgeViewBaseOpenCV 提供的抽象基类,定义了摄像头启动、停止、帧获取等功能。
JavaCameraView 是它的子类,真正实现了预览逻辑。mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);// 获取拍照按钮引用mCaptureButton = (Button) findViewById(R.id.capture_button);// 获取HDR按钮引用mHdrButton = (Button) findViewById(R.id.hdr_button);// 设置相机视图可见性mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);// 设置相机视图监听器mOpenCvCameraView.setCvCameraViewListener(this);// 设置拍照按钮点击监听器mCaptureButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 检查当前帧是否有效if (mRgba != null && !mRgba.empty()) {// 调用保存图像方法saveImage(mRgba);}}});// 设置HDR按钮点击监听器mHdrButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 设置HDR拍摄标志mIsCapturingHDR = true;// 清空曝光序列mExposureSequence.clear();// 显示提示信息Toast.makeText(Tutorial1Activity.this, "准备HDR拍摄,请保持手机稳定...",Toast.LENGTH_SHORT).show();}});}

CameraBridgeViewBase 是什么?
虽然 CameraBridgeViewBase 本质上是一个“视图组件”(View),但它不仅仅是用来显示摄像头图像,还内置了摄像头控制逻辑、帧采集机制和 OpenCV 回调接口对接。

它是 OpenCV for Android 提供的一个抽象类,负责:

功能说明
控制摄像头启动/停止封装了打开摄像头、释放摄像头资源的流程
帧采集循环自动开启后台线程采集图像帧
图像格式转换将 NV21/YUV 等图像转换为 OpenCV 可处理的 Mat 类型
接入回调让开发者通过接口拿到每一帧图像进行处理

帧采集 + 回调的完整机制

方法功能
setCvCameraViewListener(this)设置帧数据监听器,接收每一帧图像
enableView()内部会打开摄像头,启动采集线程,开始不断生成帧

OpenCV 内部怎么做的?

OpenCV 封装了以下逻辑:

  • 打开摄像头(通过 Camera / Camera2 API)

    后台线程循环采集图像帧(YUV 或 RGBA)

    转换为 Mat 类型(OpenCV 可处理格式)

    调用你实现的接口方法:

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {// 每帧图像都走这里,你可以处理它return inputFrame.rgba(); // 或处理后返回
}

对应

你的代码↓
CameraBridgeViewBase↓
JavaCameraView / NativeCameraView↓
系统 Camera API↓
图像帧采集 → 自动转换为 Mat → 回调 onCameraFrame()
    // Activity暂停时的回调方法@Overridepublic void onPause() {// 调用父类方法super.onPause();// 如果相机视图存在,则禁用视图if (mOpenCvCameraView != null)mOpenCvCameraView.disableView();}// Activity恢复时的回调方法@Overridepublic void onResume() {// 调用父类方法super.onResume();// 如果相机视图存在,则启用视图if (mOpenCvCameraView != null)mOpenCvCameraView.enableView();}// 获取相机视图列表的方法@Overrideprotected List<? extends CameraBridgeViewBase> getCameraViewList() {// 返回包含单个相机视图的不可变列表return Collections.singletonList(mOpenCvCameraView);}// Activity销毁时的回调方法@Overridepublic void onDestroy() {// 调用父类方法super.onDestroy();// 如果相机视图存在,则禁用视图if (mOpenCvCameraView != null)mOpenCvCameraView.disableView();}// 相机视图启动时的回调方法@Overridepublic void onCameraViewStarted(int width, int height) {// 初始化RGBA矩阵mRgba = new Mat();}// 相机视图停止时的回调方法@Overridepublic void onCameraViewStopped() {// 如果RGBA矩阵存在,则释放资源if (mRgba != null) {mRgba.release();}}// 处理相机帧数据的回调方法@Overridepublic Mat onCameraFrame(CvCameraViewFrame inputFrame) {// 获取当前帧的RGBA数据mRgba = inputFrame.rgba();// 返回处理后的帧return mRgba;}

保存文件

// 保存图像到文件的方法private void saveImage(Mat mat) {// 创建与Mat相同尺寸的Bitmap对象Bitmap bmp = Bitmap.createBitmap(mat.cols(), mat.rows(), Bitmap.Config.ARGB_8888);// 将Mat转换为BitmapUtils.matToBitmap(mat, bmp);// 创建时间戳格式String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());// 生成文件名String imageFileName = "IMG_" + timeStamp + ".jpg";// 获取公共图片目录File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);// 创建文件对象File imageFile = new File(storageDir, imageFileName);try {// 创建文件输出流FileOutputStream fos = new FileOutputStream(imageFile);// 压缩Bitmap为JPEG格式并写入文件bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);// 关闭输出流fos.close();// 显示保存成功提示Toast.makeText(this, "照片已保存: " + imageFile.getAbsolutePath(),Toast.LENGTH_LONG).show();} catch (Exception e) {// 记录保存失败日志Log.e(TAG, "保存照片失败", e);// 显示保存失败提示Toast.makeText(this, "保存照片失败", Toast.LENGTH_SHORT).show();}}
http://www.xdnf.cn/news/14690.html

相关文章:

  • SQL 分页方法全解析:从基础到高级应用
  • 深入解析ID3算法:信息熵驱动的决策树构建基石
  • 【Qt开发】网络运用
  • 项目中后端如何处理异常?
  • JAVA锁机制:对象锁与类锁
  • 一、什么是生成式人工智能
  • GPT-1 与 BERT 架构
  • MySQL之InnoDB存储引擎深度解析
  • 软件工程期末试卷填空题版带答案(共40道)
  • 【环境配置】在Ubuntu Server上安装5090 PyTorch环境
  • CVE-2024-6387漏洞、CVE-2025-26465漏洞、CVE-2025-26466漏洞 一口气全解决
  • 题解:P11501 [ROIR 2019] 探险队(Day 2)
  • 【软考高级系统架构论文】论无服务器架构及其应用
  • 在 `setup` 函数中使用 Vuex
  • 通过 Lambda + API Gateway + 外部 API 实现。
  • Django数据库迁移
  • LLM:重构数字世界的“智能操作系统”
  • Java面试题025:一文深入了解数据库Redis(1)
  • Docker高级管理--容器通信技术与数据持久化
  • 【ubuntu下小工具】Crontab定时任务进行数据备份和清理
  • 【AGI】突破感知-决策边界:VLA-具身智能2.0
  • 格兰泰勒棱镜透射光强曲线优化处理
  • 嵌入式开发之嵌入式系统架构如何搭建?
  • Java ArrayList集合和HashSet集合详解
  • day38 打卡
  • 基于Python、tkinter、sqlite3 和matplotlib的校园书店管理系统
  • 多线程八股
  • Shell脚本中和||语法解析
  • tkinter Text 组件学习指南
  • 创业知识概论