Android --- CameraX讲解

预备知识

surface   surfaceView   SurfaceHolder

surface 是什么?

一句话来说: surface是一块用于填充图像数据的内存。

surfaceView 是什么?

它是一个显示surface 的View。 在app中仍在 ViewHierachy 中,但在wms 中可以理解为Server端,它与宿主窗口是分离的。(它拥有独立的绘制表面 , 它不与其它宿主窗口共享一个绘制表面,可以单独在一个线程进行绘制,并不会占用主线程的资源,这样,绘制就会比较高效,游戏、视频播放,还有最近几年比较热门的直播,都可以用surface  )

surfaceHolder 是什么?

 它是一个接口,给持有surface的对象使用。可以控制surface的大小和格式。编辑surface中的像素,以及监听surface的变化这个接口通过surfaceView获取。

CameraX

CameraX是Jetpack 支持库

特点:

1、利用的是 camera2 的功能

2、它具有生命周期的感应,使用更加简单,代码量也减少了不少。可以灵活的录制视频和拍照。

3、抹平设备兼容问题。

使用

第一步:

引入依赖

dependencies {def camerax_version = "1.2.0-alpha04"implementation "androidx.camera:camera-core:${camerax_version}"implementation "androidx.camera:camera-camera2:${camerax_version}"implementation "androidx.camera:camera-lifecycle:${camerax_version}"implementation "androidx.camera:camera-video:${camerax_version}"implementation "androidx.camera:camera-view:${camerax_version}"implementation "androidx.camera:camera-extensions:${camerax_version}"
}

使用 ViewBinding,因此请使用以下代码(在 android{} 代码块末尾)启用它:

android {buildFeatures {viewBinding true}
}

第二步:

添加布局控件。   androidx.camera.view.PreViewView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><androidx.camera.view.PreviewViewandroid:id="@+id/viewFinder"android:layout_width="match_parent"android:layout_height="match_parent" /><Buttonandroid:id="@+id/image_capture_button"android:layout_width="110dp"android:layout_height="110dp"android:layout_marginEnd="50dp"android:layout_marginBottom="50dp"android:elevation="2dp"android:text="@string/take_photo"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@id/vertical_centerline" /><Buttonandroid:id="@+id/video_capture_button"android:layout_width="110dp"android:layout_height="110dp"android:layout_marginStart="50dp"android:layout_marginBottom="50dp"android:elevation="2dp"android:text="@string/start_capture"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toEndOf="@id/vertical_centerline" /><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/vertical_centerline"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintGuide_percent=".50" /></androidx.constraintlayout.widget.ConstraintLayout>

因为 build.gradle 中设置了 viewBinding = true,所以会为每个布局都生成对应的绑定类(即 activity_main.xml 自动生成 ActivityMainBinding 类)。

第三步:

设置检查相机权限:

在 MainActivity.kt 中设置检查相机权限,设置 Button 的响应事件:

package com.bignerdranch.android.cameraxappimport android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.ImageCapture
import androidx.camera.video.Recorder
import androidx.camera.video.Recording
import androidx.camera.video.VideoCapture
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.bignerdranch.android.cameraxapp.databinding.ActivityMainBinding
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executorstypealias LumaListener = (luma: Double) -> Unitclass MainActivity : AppCompatActivity() {private lateinit var viewBinding: ActivityMainBindingprivate var imageCapture: ImageCapture? = nullprivate var videoCapture: VideoCapture<Recorder>? = nullprivate var recording: Recording? = nullprivate lateinit var cameraExecutor: ExecutorServiceoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)viewBinding = ActivityMainBinding.inflate(layoutInflater)setContentView(viewBinding.root)// Request camera permissionsif (allPermissionsGranted()) {startCamera()} else {ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)}// Set up the listeners for take photo and video capture buttonsviewBinding.imageCaptureButton.setOnClickListener { takePhoto() }viewBinding.videoCaptureButton.setOnClickListener { captureVideo() }cameraExecutor = Executors.newSingleThreadExecutor()}private fun takePhoto() {}private fun captureVideo() {}private fun startCamera() {}private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED}override fun onDestroy() {super.onDestroy()cameraExecutor.shutdown()}companion object {private const val TAG = "CameraXApp"private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"private const val REQUEST_CODE_PERMISSIONS = 10private val REQUIRED_PERMISSIONS =mutableListOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO).apply { }.toTypedArray()}
}

在 AndroidManifest.xml 中申请摄像头权限,其中 android.hardware.camera.any 可确保设备配有相机。指定 .any 表示它可以是前置摄像头,也可以是后置摄像头。配置如下:

<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"android:maxSdkVersion="28" />

在 MainActivity 添加如下函数,会根据用户批准的权限,执行对应的回调函数:

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {super.onRequestPermissionsResult(requestCode, permissions, grantResults)if (requestCode == REQUEST_CODE_PERMISSIONS) {if (allPermissionsGranted()) {startCamera()} else {Toast.makeText(this, "Permissions not granted by the user", Toast.LENGTH_SHORT).show()finish()}}}

第四步:

预览 摄像头、 拍照、 图片分析

初始化cameraProviderFuture 并得到cameraProvider 。

得到cameraProvider 后初始化 PreView 、ImageCapture 、ImageAnalysis。  可以看到三个单独配置的。 所以完全解耦。

 tips:

PreView: 它是相机预览的数据流图像数据通过它来传输,比如输出到surface中。

ImageCapture : 用于拍照 这里的builder 都可以设置参数,比如 大小、曝光度。

ImageAnalysis : 用于图像分析,如二维码识别、人脸等。

预览 摄像头

MainActivity 中实现 startCamera() 函数

    private fun startCamera() {// 用于将相机的生命周期绑定到生命周期所有者(MainActivity)。 这消除了打开和关闭相机的任务,因为 CameraX 具有生命周期感知能力。val cameraProviderFuture = ProcessCameraProvider.getInstance(this)// 向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。我们会在稍后填写它。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。cameraProviderFuture.addListener({// 将相机的生命周期绑定到应用进程中的 LifecycleOwner。val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()val preview = Preview.Builder().build().also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // preview 作为 usecaseval cameraSelector = CameraSelector.DEFAULT_BACK_CAMERAtry {cameraProvider.unbindAll() // Unbind use cases before rebindingcameraProvider.bindToLifecycle(this, cameraSelector, preview) // Bind use cases to camera: 把 cameraSelector 和 preview 绑定} catch (exc: Exception) {Log.e(TAG, "Use case binding failed", exc) // 有多种原因可能会导致此代码失败,例如应用不再获得焦点。在此记录日志。}}, ContextCompat.getMainExecutor(this))}

ImageCamera 拍照

 MainActivity 中实现 takePhoto() 函数

    private fun takePhoto() {// Get a stable reference of the modifiable image capture use caseval imageCapture = imageCapture ?: return// 存图路径和参数(时间、文件类型)val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis())val contentValues = ContentValues().apply {put(MediaStore.MediaColumns.DISPLAY_NAME, name)put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")}// 我们希望将输出保存在 MediaStore 中,以便其他应用可以显示它val outputOptions =ImageCapture.OutputFileOptions.Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues).build()// 拍照后的回调函数imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this),object : ImageCapture.OnImageSavedCallback {override fun onError(exc: ImageCaptureException) {Log.e(TAG, "Photo capture failed: ${exc.message}", exc)}override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {val msg = "Photo capture succeeded: ${outputFileResults.savedUri}"Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()Log.d(TAG, msg)}})}

在 MainActivity 的 startCamera() 函数中,添加如下 imageCapture = ImageCapture.Builder().build() 来初始化摄像头的 use case,并绑定到 cameraProvider.bindToLifecycle() 中,完整代码如下。

    private fun startCamera() {// 用于将相机的生命周期绑定到生命周期所有者(MainActivity)。 这消除了打开和关闭相机的任务,因为 CameraX 具有生命周期感知能力。val cameraProviderFuture = ProcessCameraProvider.getInstance(this)// 向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。我们会在稍后填写它。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。cameraProviderFuture.addListener({// 将相机的生命周期绑定到应用进程中的 LifecycleOwner。val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()val preview = Preview.Builder().build().also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // preview 作为 usecaseimageCapture = ImageCapture.Builder().build()val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERAtry {cameraProvider.unbindAll() // Unbind use cases before rebindingcameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) // Bind use cases to camera} catch (exc: Exception) {Log.e(TAG, "Use case binding failed", exc) // 有多种原因可能会导致此代码失败,例如应用不再获得焦点。在此记录日志。}}, ContextCompat.getMainExecutor(this))}

用 ImageAnalysis 分析各帧

使用 ImageAnalysis 功能可让相机应用变得更加有趣。它允许定义实现 ImageAnalysis.Analyzer 接口的自定义类,并使用传入的相机帧调用该类。我们无需管理相机会话状态,甚至无需处理图像;与其他生命周期感知型组件一样,仅绑定到应用所需的生命周期就足够了。

    private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {private fun ByteBuffer.toByteArray(): ByteArray {rewind()    // Rewind the buffer to zeroval data = ByteArray(remaining())get(data)   // Copy the buffer into a byte arrayreturn data // Return the byte array}override fun analyze(image: ImageProxy) {val buffer = image.planes[0].bufferval data = buffer.toByteArray()val pixels = data.map { it.toInt() and 0xFF }val luma = pixels.average()listener(luma)image.close()}}

然后,在 startCamera() 函数中,实例化 imageAnalyzer 对象,通过 setAnalyzer() 设置其回调函数来打印 luma(亮度),并绑定到 cameraProvider.bindToLifecycle() 上,代码如下:

    private fun startCamera() {// 用于将相机的生命周期绑定到生命周期所有者(MainActivity)。 这消除了打开和关闭相机的任务,因为 CameraX 具有生命周期感知能力。val cameraProviderFuture = ProcessCameraProvider.getInstance(this)// 向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。我们会在稍后填写它。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。cameraProviderFuture.addListener({// 将相机的生命周期绑定到应用进程中的 LifecycleOwner。val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()val preview = Preview.Builder().build().also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // preview 作为 usecaseimageCapture = ImageCapture.Builder().build()val imageAnalyzer = ImageAnalysis.Builder().build().also {it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->Log.d(TAG, "Average luminosity: $luma")})}val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERAtry {cameraProvider.unbindAll() // Unbind use cases before rebindingcameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, imageAnalyzer) // Bind use cases to camera} catch (exc: Exception) {Log.e(TAG, "Use case binding failed", exc) // 有多种原因可能会导致此代码失败,例如应用不再获得焦点。在此记录日志。}}, ContextCompat.getMainExecutor(this))}

其实是通过 LuminosityAnalyzer.analyze() 函数内的 listener(luma) 将 luma 参数传给 listener() 函数,然后我们通过 setAnalyzer() 自定义了 listener() 函数,其接收亮度,并通过 Logcat 打印。

用 VideoCapture 录像

 MainActivity 中实现 captureVideo() 

    private fun captureVideo() {val videoCapture = this.videoCapture ?: returnviewBinding.videoCaptureButton.isEnabled = falseval curRecording = recordingif (curRecording != null) {curRecording.stop()recording = nullreturn}// create and start a new recording sessionval name = SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis())val contentValues = ContentValues().apply {put(MediaStore.MediaColumns.DISPLAY_NAME, name)put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")}val mediaStoreOutputOptions = MediaStoreOutputOptions.Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI).setContentValues(contentValues).build()recording = videoCapture.output.prepareRecording(this, mediaStoreOutputOptions).apply {if (PermissionChecker.checkSelfPermission(this@MainActivity, Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) {withAudioEnabled()}}.start(ContextCompat.getMainExecutor(this)) { recordEvent ->when (recordEvent) {is VideoRecordEvent.Start -> {viewBinding.videoCaptureButton.apply {text = getString(R.string.stop_capture)isEnabled = true}}is VideoRecordEvent.Finalize -> {if (!recordEvent.hasError()) {val msg = "Video capture succeeded: ${recordEvent.outputResults.outputUri}"Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()Log.d(TAG, msg)} else {recording?.close()recording = nullLog.e(TAG, "Video capture ends with error: ${recordEvent.error}")}viewBinding.videoCaptureButton.apply {text = getString(R.string.start_capture)isEnabled = true}}}}}

然后,在 MainActivity 的 startCamera() 函数中,将 videoCapture 绑定到 cameraProvider.bindToLifecycle() 函数中,因为camera 同时只能绑定3种use case,所以本节在拍照、摄像、预览、分析中,选择了前3种用途,

   private fun startCamera() {// 用于将相机的生命周期绑定到生命周期所有者(MainActivity)。 这消除了打开和关闭相机的任务,因为 CameraX 具有生命周期感知能力。val cameraProviderFuture = ProcessCameraProvider.getInstance(this)// 向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。我们会在稍后填写它。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。cameraProviderFuture.addListener({// 将相机的生命周期绑定到应用进程中的 LifecycleOwner。val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()val preview = Preview.Builder().build().also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // preview 作为 use caseimageCapture = ImageCapture.Builder().build()
//            val imageAnalyzer = ImageAnalysis.Builder().build().also {
//                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
//                    Log.d(TAG, "Average luminosity: $luma")
//                })
//            }val recorder = Recorder.Builder().setQualitySelector(QualitySelector.from(Quality.HIGHEST)).build()videoCapture = VideoCapture.withOutput(recorder)val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERAtry {cameraProvider.unbindAll() // Unbind use cases before rebindingcameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, videoCapture) // Bind use cases to camera} catch (exc: Exception) {Log.e(TAG, "Use case binding failed", exc) // 有多种原因可能会导致此代码失败,例如应用不再获得焦点。在此记录日志。}}, ContextCompat.getMainExecutor(this))}

​​​​​​​

结束。

参考: 

https://juejin.cn/post/7354922285092847668

https://juejin.cn/post/7354937037391986722

使用Preview预览和自定义SurfaceView或者TextureView实现CameraX预览功能_preview.setsurfaceprovider-CSDN博客

Android之SurfaceView和TextureView的分析_surfaceview和textview的区别-CSDN博客​​​​​​​
 

在Android CameraX中,PreviewView和SurfaceView的关系主要体现在它们都可以作为相机的预览显示视图,但PreviewView提供了更多的功能和灵活性。

PreviewView和SurfaceView的区别和联系

  1. 功能差异‌:

    • PreviewView‌:这是一个专门为CameraX设计的视图,支持裁剪、缩放和旋转,确保预览的正确显示。它提供了更多的控制和灵活性,例如可以设置不同的预览分辨率和帧率‌1。
    • SurfaceView‌:是一个更传统的视图,用于展示相机预览图像。它不需要布局文件中的声明,可以直接在代码中创建和使用。SurfaceView适合简单的预览需求,但在CameraX中,PreviewView是更推荐的选择‌12。
  2. 使用场景‌:

    • PreviewView‌:适用于需要高度自定义预览显示的场景,如实时视频处理、预览画面的特殊效果等。它能够更好地与CameraX的其他组件集成,提供更好的用户体验‌1。
    • SurfaceView‌:适用于简单的预览需求,不需要复杂的预览处理。虽然SurfaceView在CameraX中不是首选,但在一些旧的项目或特定的需求中仍然可以使用‌12。

如何在CameraX中使用PreviewView

  1. 添加PreviewView到布局文件‌:

     

    xmlCopy Code

    <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.camera.view.PreviewView android:id="@+id/previewView" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>

  2. 初始化ProcessCameraProvider并绑定PreviewView‌:

     

    kotlinCopy Code

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState == null) {

Android OpenGL ES 学习(一) -- 基本概念-CSDN博客

Android 音视频编解码(一) -- MediaCodec 初探_安卓音视频解码-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10671.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP深度学习 DAY5:Sequence-to-sequence 模型详解

Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种用于处理输入和输出均为序列任务的深度学习模型。它最初被设计用于机器翻译&#xff0c;但后来广泛应用于其他任务&#xff0c;如文本摘要、对话系统、语音识别、问答系统等。 核心思想 Seq2Seq 模型的目标是将…

Java锁自定义实现到aqs的理解

专栏系列文章地址&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标&#xff1a; 理解锁&#xff0c;能自定义实现锁通过自定义锁的实现复习Thread和Object的相关方法开始尝试理解Aqs, 这样后续基于Aqs的的各种实现将能更好的理解 目录 锁的…

基于STM32的阿里云智能农业大棚

目录 前言&#xff1a; 项目效果演示&#xff1a; 一、简介 二、硬件需求准备 三、硬件框图 四、CubeMX配置 4.1、按键、蜂鸣器GPIO口配置 4.2、ADC输入配置 4.3、IIC——驱动OLED 4.4、DHT11温湿度读取 4.5、PWM配置——光照灯、水泵、风扇 4.6、串口——esp8266模…

【游戏设计原理】96 - 成就感

成就感是玩家体验的核心&#xff0c;它来自完成一件让自己满意的任务&#xff0c;而这种任务通常需要一定的努力和挑战。游戏设计师的目标是通过合理设计任务&#xff0c;不断为玩家提供成就感&#xff0c;保持他们的参与热情。 ARCS行为模式&#xff08;注意力、关联性、自信…

MySQL CTE:解锁SQL查询新模式

目录 一、CTE 初相识 二、CTE 基础语法 &#xff08;一&#xff09;基本语法结构 &#xff08;二&#xff09;语法规则详解 三、非递归 CTE 应用实例 &#xff08;一&#xff09;单 CTE 简单查询 &#xff08;二&#xff09;多 CTE 联合查询 四、递归 CTE 深入探索 &…

C#,入门教程(12)——数组及数组使用的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(11)——枚举&#xff08;Enum&#xff09;的基础知识和高级应用https://blog.csdn.net/beijinghorn/article/details/123917587https://blog.csdn.net/beijinghorn/article/details/123917587 数组是一种数据集合&#xff0c;是一组…

【leetcode练习·二叉树】计算完全二叉树的节点数

本文参考labuladong算法笔记[拓展&#xff1a;如何计算完全二叉树的节点数 | labuladong 的算法笔记] 如果让你数一下一棵普通二叉树有多少个节点&#xff0c;这很简单&#xff0c;只要在二叉树的遍历框架上加一点代码就行了。 但是&#xff0c;力扣第第 222 题「完全二叉树的…

低代码系统-产品架构案例介绍、轻流(九)

轻流低代码产品定位为零代码产品&#xff0c;试图通过搭建来降低企业成本&#xff0c;提升业务上线效率。 依旧是从下至上&#xff0c;从左至右的顺序 名词概述运维层底层系统运维层&#xff0c;例如上线、部署等基础服务体系内置的系统能力&#xff0c;发消息、组织和权限是必…

对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;企业与顾客之间的交互方式变得日益多样化&#xff0c;移动设备、社交媒体、门店、电子商务网站等交互点应运而生。这些交互点不仅为顾客提供了便捷的服务体验&#xff0c;同时也为企业积累了大量的顾客行为数据。本文旨在…

MSA Transformer

过去的蛋白质语言模型以单个序列为输入&#xff0c;MSA Transformer以多序列比对的形式将一组序列作为输入。该模型将行和列注意力交织在输入序列中&#xff0c;并在许多蛋白质家族中使用mask语言建模目标进行训练。模型的性能远超过了当时最先进的无监督学习方法&#xff0c;其…

QT实现有限元软件操作界面

本系列文章致力于实现“手搓有限元&#xff0c;干翻Ansys的目标”&#xff0c;基本框架为前端显示使用QT实现交互&#xff0c;后端计算采用Visual Studio C。 本篇将二维矩形截面梁单元&#xff08;Rect_Beam2D2Node&#xff09;组成的钢结构桥作为案例来展示软件功能。 也可以…

推荐一款好用的翻译类浏览器扩展插件

给大家推荐一款实用的翻译工具——沉浸式翻译。这是一款免费、高效的AI驱动浏览器扩展插件&#xff0c;能够帮助用户轻松打破语言障碍&#xff0c;享受沉浸式的阅读体验。 主要特性 沉浸式阅读体验&#xff1a;通过智能识别网页主内容区域并进行双语对照翻译&#xff0c;让用户…

ElasticSearch-文档元数据乐观并发控制

文章目录 什么是文档&#xff1f;文档元数据文档的部分更新Update 乐观并发控制 最近日常工作开发过程中使用到了 ES&#xff0c;最近在检索资料的时候翻阅到了 ES 的官方文档&#xff0c;里面对 ES 的基础与案例进行了通俗易懂的解释&#xff0c;读下来也有不少收获&#xff0…

开源的瓷砖式图像板系统Pinry

简介 什么是 Pinry &#xff1f; Pinry 是一个开源的瓷砖式图像板系统&#xff0c;旨在帮助用户轻松保存、标记和分享图像、视频和网页。它提供了一种便于快速浏览的格式&#xff0c;适合喜欢整理和分享多种媒体内容的人。 主要特点 图像抓取和在线预览&#xff1a;支持从网页…

Java 大视界 -- Java 大数据在自动驾驶中的数据处理与决策支持(68)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

【数据结构】初识链表

顺序表的优缺点 缺点&#xff1a; 中间/头部的插入删除&#xff0c;时间复杂度效率较低&#xff0c;为O(N) 空间不够的时候需要扩容。 如果是异地扩容&#xff0c;增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间&#xff0c;会有不小的消耗。 扩容可能会存在…

I.MX6ULL 中断介绍上

i.MX6ULL是NXP&#xff08;原Freescale&#xff09;推出的一款基于ARM Cortex-A7内核的微处理器&#xff0c;广泛应用于嵌入式系统。在i.MX6ULL中&#xff0c;中断&#xff08;Interrupt&#xff09;是一种重要的机制&#xff0c;用于处理外部或内部事件&#xff0c;允许微处理…

4-图像梯度计算

文章目录 4.图像梯度计算(1)Sobel算子(2)梯度计算方法(3)Scharr与Laplacian算子4.图像梯度计算 (1)Sobel算子 图像梯度-Sobel算子 Sobel算子是一种经典的图像边缘检测算子,广泛应用于图像处理和计算机视觉领域。以下是关于Sobel算子的详细介绍: 基本原理 Sobel算子…

苍穹外卖——数据统计

在商家管理端的左侧&#xff0c;有一个名为"数据统计"的菜单&#xff0c;该页面负责展示各个维度的数据统计&#xff0c;分别是营业额统计、用户统计、订单统计、销量排名top10。统计的数据是借助一些图形化的报表技术来生成并展示的。在左上角还可选择时间段&#x…

优盘恢复原始容量工具

买到一个优盘&#xff0c;显示32mb&#xff0c;我见过扩容盘&#xff0c;但是这次见到的是缩容盘&#xff0c;把2g的容量缩成32MB了&#xff0c;首次见到。。用芯片查询工具显示如下 ChipsBank(芯邦) CBM2199E 使用以下工具&#xff0c;恢复原始容量。。 其他CMB工具可能不行…