基于Android设备获取USB外接摄像头的图像

2023-05-16

目录

  • 背景
  • 开发环境
    • 硬件
    • 软件
  • 简介
  • 那我们开始吧
    • 导入项目
    • 布局、权限与初始化
    • MainActivity.java
    • 运行时遇到的问题
  • 运行结果
  • 其他

背景

  本人在读研期间接到的项目,需要用一个工业内窥镜(支持USBType-C接口)外接到 Android 设备上,获取其中图像进行目标检测等后续需求。本文主要讲Android设备如何显示、获取USB摄像头采集到的每一帧图像,后续可以通过深度学习或者Android版本的OpenCV进行目标检测。
   查阅目前的技术博客,得到了几位大牛的文章指点,最终成功完成了项目。所以本文期望在前辈的基础上进行总结,给后来者提供一些帮助。
  因为行文时作者还是在校学生,接触Android时间也不算长,可能用方法比较繁琐笨拙,如有不对处,望指正。

开发环境

硬件

荣耀V10手机一台(鸿蒙OS)、Redmi K30 PRO(Android 11)、工业内窥镜(支持USBType-C接口)

软件

Android studio 4.2.1

简介

UVC全称为USB Video Class,直接翻译过来的意思就是:USB视频类,它是一种专门为USB视频捕获设备定义的协议标准。
这个标准是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,已经成为USB org标准之一。
现在的主流操作系统,都已提供UVC设备驱动,因此符合UVC规格的硬件设备在不需要安装任何的驱动程序下即可在主机中正常使用。是的,目前Android系统已经支持uvc设备。(摘自小驰笔记:https://www.jianshu.com/p/972e05fa76a3)

本文实现的功能使参考三位大牛的文章或源码。

  • UVCCamera 开源项目
    里面有8个例程(从易到难),需要引用作者自己的 libuvccamera 库,有参考价值。(https://github.com/saki4510t/UVCCamera)

  • 博主 小驰笔记 的开源项目
    简书博主“小驰嘻嘻”的项目,小驰博主的文章要好好读一下,里面也是用到上面的 libuvccamera 库,但是里面用到了AIDL,像我这样的新手看不懂。(捂脸)(https://github.com/yorkZJC/UvcCameraDemo)

  • 博主 jiangdongguo 的开源项目
    这里面需要引入博主自己的 libuvccamera 库,不能用 UVCCamera 原作者的库,照着他的 demo 编写程序,是可以在自己的项目中实现USB摄像头预览的。(GitHub源码地址:https://github.com/jiangdongguo/AndroidUSBCamera)

最终本文参考了博主 jiangdongguo 的开源项目,并在博主无名之辈FTER文章的指导下完成。(https://blog.csdn.net/andrexpert/article/details/78324181)

那我们开始吧

导入项目

下载博主 jiangdongguo 的开源项目,在自己的项目中引入该项目的 libuvccamera 模块。
在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/72f3ce94ccf0475db1216346569cbf73.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWUguS29uZw==,size_20,color_FFFFFF,t_70,g_se,x_1
接下来会遇到一个问题
在这里插入图片描述
参考博客 https://blog.csdn.net/shenggaofei/article/details/98055433 在project目录下的build.gradle对应位置添加以下代码解决(版本号比较低,所以为黄色,不影响运行)

classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'

在这里插入图片描述
下一个问题
在这里插入图片描述
原因是 libuvccamera 模块中的各个版本已经定义为变量,而我们引入的时候没有给这些变量赋值,于是在开源项目中,找到声明版本号的地方,复制到自己项目的对应位置(也可以自己定义),之后重新同步一下,所有报错就消失了。
在这里插入图片描述
最后一步
在自己项目的 build.gradle 中添加依赖,否则 UVCCameraTextureView 图像预览控件将无法使用。

implementation project(':libusbcamera')

在这里插入图片描述

布局、权限与初始化

布局根据需求来吧,本文项目既要实时预览外接 USB相机拍摄的图像,又需要对图像进行分析,故首先把预览图像的控件摆上。 libuvccamera 模块中有一个定义好的控件 UVCCameraTextureView 直接拿来用就行。本文demo只有一个MainActivity,布局如下:

<?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">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.serenegiant.usb.widget.UVCCameraTextureView
            android:id="@+id/camera_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />

    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

权限方面,也按照开源项目配置好即可,在 AndroidManifest.xml 中加入以下权限:

	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="30"
        tools:ignore="ScopedStorage"
        />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <uses-feature android:name="android.hardware.usb.host" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

如果报红,就按 alt+Enter 解决
初始化方面,在之前下载的 jiangdongguo 的开源项目 里面找到一个叫 CrashHandler.java 的文件,拷贝到自己的项目中,会出现两处报错。

第12行报错,源于原作者引用自己编写的类所在文件夹的名字,由于类没有拷贝过来而报错,删除这行即可。
136行就是该类不存在而报错,可以改为自己的类的名字,也可以干脆删除MyApplication.DIRECTORY_NAME,让日志文件直接存放在根目录下,日志文件会存放调试时候出现的bug。

还有一个地方需要注意,由于 Android 10 以上系统不支持静态的写权限,所以需要把 build.gradle 中 compileSdkVersion 和 targetSdkVersion 均改到 28 及以下。(这个问题不会在编译期报错,只会在插入USB摄像头的时候弹出错误提示,并且无法正常退出程序。困扰了我好久,看来还是对Android了解得不够多…)

MainActivity.java

package com.example.myapplication;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.hardware.usb.UsbDevice;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.StrictMode;
import android.view.Surface;
import android.view.View;
import android.widget.Toast;

import com.jiangdg.usbcamera.UVCCameraHelper;
import com.jiangdg.usbcamera.utils.FileUtils;
import com.serenegiant.usb.CameraDialog;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.widget.CameraViewInterface;

public class MainActivity extends AppCompatActivity implements CameraDialog.CameraDialogParent, CameraViewInterface.Callback {
    private CrashHandler mCrashHandler;
    public View mTextureView;
    private UVCCameraHelper mCameraHelper;
    private CameraViewInterface mUVCCameraView;
    private boolean isRequest;
    private boolean isPreview;

    private UVCCameraHelper.OnMyDevConnectListener listener = new UVCCameraHelper.OnMyDevConnectListener() {
        @Override
        public void onAttachDev(UsbDevice device) {
            // request open permission
            if (!isRequest) {
                isRequest = true;
                if (mCameraHelper != null) {
                    mCameraHelper.requestPermission(0);
                }
            }
        }

        @Override
        public void onDettachDev(UsbDevice device) {
            // close camera
            if (isRequest) {
                isRequest = false;
                mCameraHelper.closeCamera();
                showShortMsg(device.getDeviceName() + " is out");
            }
        }

        @Override
        public void onConnectDev(UsbDevice device, boolean isConnected) {
            if (!isConnected) {
                showShortMsg("fail to connect,please check resolution params");
                isPreview = false;
            } else {
                isPreview = true;
                showShortMsg("connecting");
                // initialize seekbar
                // need to wait UVCCamera initialize over
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(30);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Looper.prepare();
                        Looper.loop();
                    }
                }).start();
            }
        }

        @Override
        public void onDisConnectDev(UsbDevice device) {
            showShortMsg("disconnecting");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AccessRequest();

        /**------- USBCamera引入 ---------*/
        mCrashHandler = CrashHandler.getInstance();
        mCrashHandler.init(getApplicationContext(), getClass());

        mTextureView = findViewById(R.id.camera_view);

        // step.1 initialize UVCCameraHelper
        mUVCCameraView = (CameraViewInterface) mTextureView;
        mUVCCameraView.setCallback(this);
        mCameraHelper = UVCCameraHelper.getInstance();
        mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_MJPEG);
        mCameraHelper.initUSBMonitor(this, mUVCCameraView, listener);

        mCameraHelper.setOnPreviewFrameListener(new AbstractUVCCameraHandler.OnPreViewResultListener() {
            @Override
            public void onPreviewResult(byte[] data) {
                // 获取单帧图像回调
            }
        });
    }

    public boolean isCameraOpened() {
        return mCameraHelper.isCameraOpened();
    }

    private void showShortMsg(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSurfaceCreated(CameraViewInterface view, Surface surface) {
        if (!isPreview && mCameraHelper.isCameraOpened()) {
            mCameraHelper.startPreview(mUVCCameraView);
            isPreview = true;
        }
    }

    @Override
    public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height) {

    }

    @Override
    public void onSurfaceDestroy(CameraViewInterface view, Surface surface) {
        if (isPreview && mCameraHelper.isCameraOpened()) {
            mCameraHelper.stopPreview();
            isPreview = false;
        }
    }

    @Override
    public USBMonitor getUSBMonitor() {
        return mCameraHelper.getUSBMonitor();
    }

    @Override
    public void onDialogResult(boolean canceled) {
        if (canceled) {
            showShortMsg("取消操作");
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        // step.2 register USB event broadcast
        if (mCameraHelper != null) {
            mCameraHelper.registerUSB();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

    }

    @Override
    protected void onStop() {
        super.onStop();
        // step.3 unregister USB event broadcast
        if (mCameraHelper != null) {
            mCameraHelper.unregisterUSB();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        FileUtils.releaseFile();
        // step.4 release uvc camera resources
        if (mCameraHelper != null) {
            mCameraHelper.release();
        }
    }

    /**
     * 检查权限 方法
     */
    private boolean checkPermission() {
        //是否有权限
        boolean haveCameraPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
        boolean haveWritePermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        boolean haverReadPermission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        return haveCameraPermission && haveWritePermission && haverReadPermission;
    }

    /**
     * 请求权限 方法
     */
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void requestPermissions() {
        requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, Manifest.permission.SYSTEM_ALERT_WINDOW,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
    }

    private void AccessRequest() {
        //动态权限检测和申请
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//大于Android 6.0
            if (!checkPermission()) { //没有或没有全部授权
                requestPermissions(); //请求权限
            }
        }

        //加 StrictMode, Android 7.0以后,获取文件Uri需要加上这么一段
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
            StrictMode.setVmPolicy(builder.build());
        }
    }

}

运行时遇到的问题

编译时又出现了问题
在这里插入图片描述
貌似找不到一个名叫 libusbcommon_v4.1.1 的文件。
这个文件在引入项目的时候其实已经就放在libuvccamera 模块中,把整个libs文件夹拷贝到自己的项目文件中,如下图所示。
在这里插入图片描述
然后在 build.gradle 中引入这个文件夹在这里插入图片描述

运行结果

在这里插入图片描述

其他

在这里可以调整摄像头的分辨率等设置。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Android设备获取USB外接摄像头的图像 的相关文章

  • Settings 添加一级菜单

    Settings添加一级菜单 xff1a 1 一级菜单项的实现是Activity 例如MySettings java xff0c 此类文件直接继承的是Activity xff0c 添加比较简单 xff08 1 xff09 在清单文件中添加如
  • Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 4171 异常的解决方法

    在做本地json文件的解析时遇到了这个问题 原代码为 64 RequestMapping value 61 34 readJson1 34 public String readJson1 String cityJsonCode json解析
  • Visual Studio 中 Tab 转换为空格的设置

    在 Visual Studio 中写代码时 xff0c 按 Tab 键 xff0c 会自动进行缩进 有时希望实现按 Tab 键 xff0c 出现多个空格的效果 Visual Studio 提供了这样的功能 xff0c 具体设置方法为 xff
  • 剑指offer—03

    剑指 Offer 03 数组中重复的数字 找出数组中重复的数字 在一个长度为 n 的数组 nums 里的所有数字都在 0 xff5e n 1 的范围内 数组中某些数字是重复的 xff0c 但不知道有几个数字重复了 xff0c 也不知道每个数
  • JSONArray.remove(index)失败原因分析

    集合在执行remove方法的时候 xff0c 有两种执行方式 xff0c 第一种移除对象remove xff08 object xff09 xff0c 另一种根据下标移除remove xff08 intIndex xff09 错误案例 Li
  • 【批处理bat】暂停功能命令

    一 目的 对暂停功能做修改 二 功能 2 1屏蔽 pause gt nul 在原本的pause上使用右尖括号写入nul即可不显示任何内容 2 2修改 echo press anykey to continue XD 在pause前利用ech
  • AOSP的编译及刷机

    简介 众所周知 xff0c Android是开源的 xff0c AOSP xff08 Android Open Source Project xff09 为Android开源项目的缩写 作为一名Android开发 xff0c 掌握Andro
  • Linux常用命令记录(du、find、grep、hadoop/hdfs、sed、tar、tr)

    Linux常用命令 查询格式 语句1 语句2 语句3 xff1a 对语句1的输出结果进行语句2的判定 xff0c 然后对输出结果进行语句3的判定 如 xff1a cat a txt head 10 wc l 39 cat a txt 39
  • 虚拟机运行出现蓝屏的现象如何解决

    前两天给大家分享了如何在电脑上安装虚拟机 xff0c 听到有部分小朋友私信跟我反馈说 xff0c 自己本身电脑可以安装vm虚拟机但是他安装过后一运行就立马进入蓝屏修复界面 所以今天想跟大家分享一下遇见这种情况如何解决 xff08 本文以华硕
  • 小白也能学懂——子网划分(2)

    我前天讲了一下子网划分 xff0c 昨天比较忙碌就忘记写剩下的内容了 xff0c 今天吃过饭 xff0c 想给他补上 xff0c 主要还是细分一下子网划分的作用 xff0c 以及如果进行计算 xff0c 本章还不是算难 xff0c 但是计算
  • 三分钟告诉你什么是三层交换机!

    昨天上周我们讲了单臂路由和跨交换机传输 xff0c 今天想说一下三层交换机 xff0c 对了还有个小实验 xff0c 收到反馈说我每次都是在图里标注代码不够清晰 xff0c 所以接下来会在实际中把代码贴出来供大家复制使用 目录 一 三层交换
  • 链路聚合(二层链路和三层链路)

    昨天主要介绍了三层交换机 xff0c 今天顺其自然就讲到了链路聚合 xff0c 因为是交换机中一个比较重要的技术 xff0c 下面我们开始 目录 一 单臂路由和三层交换的复习 二 端口绑定技术 三 链路聚合 端口聚合 端口绑定实现的条件 四
  • 静态路由(也许是目前最全的)

    今天在公司 xff0c 新来了个实习生 xff0c 突然问道静态路由的问题 xff0c 他跟我讲他不会设置 然后我就很尴尬 xff0c 因为这个毕竟是基础知识嘛 所以今天整理了一下静态路由的知识 xff0c 跟大家分享一下 目录 一 路由器
  • C# 读取Json文件--代码示例

    1 C 读取Json文件 JsonConvert SerializeObject str object to string JsonConvert DeserializeObject obj string to json 2 Json文件创
  • 网络地址转换协议——NAT(恐怕是最全的版本)

    前天我说第二天要跟大家讲一下NAT的 xff0c 结果放假有些懒 xff0c 所以就放在今天更新 xff0c 希望大家不要凶我 xff0c 哈哈哈 目录 一 什么是NAT 1 NAT简介 2 NAT作用 3 NAT内网地址的范围 4 主要应
  • linux日志文件详解

    目录 一 日志文件的分类二 日志文件位置三 常见日志文件1 分析日志文件2 内核及系统日志 四 日志消息等级五 日志文件分析1 用户日志2 程序日志 六 日志分析注意事项 一 日志文件的分类 日志文件是用于记录Linux系统中各种运行消息的
  • 虚拟化与docker基础

    文章目录 一 虚拟化1 虚拟化概述2 虚拟化的功能3 虚拟化的三种模式4 容器与虚拟化 二 Docker1 容器概述2 Docker概述3 Docker的设计宗旨4 容器与虚拟机的区别5 容器在内核中支持两种重要的技术6 Docker核心概
  • Docker容器网络模式与数据管理

    文章目录 一 Docker容器操作1 容器创建2 查看容器的运行状态3 启动容器4 创建并开启容器5 终止容器运行6 容器的进入7 复制文件到容器中 宿主机中8 容器的导出与导入9 删除容器 二 Docker网络1 Docker网络实现原理
  • docker镜像的创建与dockerfile

    文章目录 一 docker镜像的创建1 创建镜像的方法2 基于现有镜像创建3 基于本地模板创建4 基于dockerfile创建 二 Dockerfile1 概述2 Dockerfile结构3 Dockerfile镜像结构的分层4 Docke
  • matlab中值滤波实现

    中值滤波是一种典型的非线性滤波 xff0c 是基于排序统计理论的一种能够有效抑制噪声的非线性信号处理技术 xff0c 基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值 xff0c 让周围的像素值接近真实的值从而消除孤立的噪声点 该方

随机推荐

  • 程序员的情人节

    今天是一个好的节日 xff0c 七夕呀 xff01 程序是最好的女朋友 xff0c 它是不会骗你的 它偶尔会发些小的情绪 只是你没有懂它
  • stm32-Hardfault及内存溢出的查找方法

    STM32内存结构 1 要点 1 1 两种存储类型 RAM 和 Flash RAM可读可写 xff0c 在STM32的内存结构上 xff0c RAM地址段分布 0x2000 0000 0x2000 0000 43 RAM size Flas
  • raylib部分源代码功能解读

    官网 https www raylib com https github com raysan5 raylib 我根据自己的需求裁剪了多余功能后的代码 xff1a https gitee com xd15zhn raylib https g
  • 无量纲处理、量纲变换与实时仿真理论

    基本原理 万有引力公式 d 2 r
  • 局域网windows平台下时间同步

    最近单位出现很多应为系统时间不统一造成的问题 xff0c 如 客户机时间与服务器时间不同步 xff0c 而客户机使用软件是读取本机时间上传服务器 xff0c 这样就会造成排序错误 每次开机修改很繁琐 我就想到了在局域网内假设时间服务器的想法
  • 水下潜航器的建模与控制

    线性系统理论大作业 待完成 题目 水下潜器模型 xff0c 可能是潜艇或者鱼雷等对象 一个主推进螺旋桨 xff0c 前后两对水平陀翼 xff0c 后面一对垂直陀翼 潜器前进过程中 xff0c 通过调节助推进螺旋桨推力 xff0c 以及三对陀
  • 演化博弈、复制动态方程与仿真

    本文只整理和总结一下我的理解 xff0c 文末列出了可供参考的更详细完整的资料 建议先看参考资料 1 xff08 博弈论公开课 xff09 的博弈论课程 xff0c 可以直接从第11讲开始看 参考链接 2 是关于演化博弈非常经典的一本书 参
  • 演化博弈方法用于多智能体系统最优资源分配

    演化博弈方法用于多智能体系统最优资源分配 Evolutionary game theoretic approach for optimal resource allocation in multi agent systems 论文复现见 论
  • [论文复现]演化博弈方法用于多智能体系统最优资源分配

    原文 演化博弈方法用于多智能体系统最优资源分配 CSDN博客 https ieeexplore ieee org document 8243778 问题描述 有2种资源分配给6个个体 xff0c 2种资源的总量分别为 y 1 61 545
  • 基于博弈学习的分布式卫星任务规划

    基于博弈学习的分布式卫星任务规划 Distributed Satellite Mission Planning via Learning in Games 摘要 对地观测卫星群的任务规划是一个复杂的问题 xff0c 它提出了重大的理论和技术
  • 多星分布式任务分配中的博弈自组织

    多星分布式任务分配中的博弈自组织 Game theoretic self organization in multi satellite distributed task allocation 论文复现见 论文复现 多星分布式任务分配中的博
  • 自用的矩阵运算库zhnmat使用说明

    自用的矩阵运算库zhnmat使用说明 包含两个主要类 xff1a Mat和Vector3d xff0c 可以用于一些简单的矩阵和三维向量场景 xff0c 代码较简单 xff0c 没有任何性能优化 xff0c 可用于学习参考 代码仓库 htt
  • 非线性系统的反馈线性化

    仿射非线性或非仿射非线性指对输入是否是线性的 例如 xff0c 系统能够写成 x 61 f x
  • 基于特征模型的全系数自适应控制

    摘要 xff1a 首先推导了全系数和等于1的证明过程 xff0c 分析了等效时间常数的概念 xff0c 然后推导了递推最小二乘公式并用于参数辨识的方法 xff0c 最后给几个仿真的例子 全系数之和等于1 被控对象用微分方程 y n
  • raylib一些示例代码

    摘要 xff1a 几种相机视角 3D预览图视角 xff1a 鼠标拖动 xff0c 滚轮缩放 只能沿着中心点 第一视角 xff1a WSAD分别控制视角的前后左右移动 xff0c EQ分别控制上下移动 xff0c 滚轮控制移动速度 画正方体线
  • gnome session 中的开机启动程序配置文件

    我找了很久才找到的 xff1a 管理员状态下 xff1a linux Blue home library config autostart ls gnome terminal desktop stardict desktop 我开机启动的两
  • 使用深度Q网络(Deep Q Network)学习控制倒立摆

    使用深度Q网络 Deep Q Network 学习控制单摆 原文 xff1a https qiita com ashitani items bb393e24c20e83e54577 摘要 xff1a 我们将尝试使用Deep Q网络 xff0
  • python多次调用exe文件运行不同的结果

    摘要 xff1a 有个C 43 43 项目是读取配置参数文件并打印对应的结果 xff0c 后来需要多次修改配置文件并运行 xff0c 于是想到写个python脚本执行这一过程 写一个测试项目 xff0c 项目结构如下 xff1a 根目录 m
  • Python通过SWIG调用C++时出现的ImportError问题解析

    摘要 win10系统 xff0c 编译器为mingw xff0c 按照教程封装C 43 43 的一个类并用python调用 xff0c 一步步进行直到最后一步运行python代码时 xff0c 在python代码中import exampl
  • 基于Android设备获取USB外接摄像头的图像

    目录 背景开发环境硬件软件 简介那我们开始吧导入项目布局 权限与初始化MainActivity java运行时遇到的问题 运行结果其他 背景 本人在读研期间接到的项目 xff0c 需要用一个工业内窥镜 xff08 支持USB和Type C接