Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互

2023-05-16

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:gzzaigcn2009@163.com

Software:系统源码Android5.1


前沿:

在全新的Camera API2架构下,常常会有人疑问再也看不到熟悉的SetParameter/Paramters等相关的身影,取而代之的是一种全新的CameraMetadata结构的出现,他不仅很早就出现在Camera API1/API2结构下的Camera2Device、Camera3Device中用于和HAL3的数据交互,而现在在API2的驱使下都取代了Parameter,实现了Java到native到hal3的参数传递。那么现在假如需要在APP中设置某一项控制参数,对于Camera API2而言,涉及到对Sensor相关参数的set/control时又需要做哪些工作呢?


1. camera_metadata类整体布局结构

主要涉及到的源文件包括camera_metadata_tags.h,camera_metadata_tag_info.c,CameraMetadata.cpp,camera_metadata.c。对于每个Metadata数据,其通过不同业务控制需求,将整个camera工作需要的参数划分成多个不同的Section,其中在camera_metadata_tag_info.c表定义了所有Camera需要使用到的Section段的Name:

const char *camera_metadata_section_names[ANDROID_SECTION_COUNT] = {
    [ANDROID_COLOR_CORRECTION]     = "android.colorCorrection",
    [ANDROID_CONTROL]              = "android.control",
    [ANDROID_DEMOSAIC]             = "android.demosaic",
    [ANDROID_EDGE]                 = "android.edge",
    [ANDROID_FLASH]                = "android.flash",
    [ANDROID_FLASH_INFO]           = "android.flash.info",
    [ANDROID_GEOMETRIC]            = "android.geometric",
    [ANDROID_HOT_PIXEL]            = "android.hotPixel",
    [ANDROID_HOT_PIXEL_INFO]       = "android.hotPixel.info",
    [ANDROID_JPEG]                 = "android.jpeg",
    [ANDROID_LENS]                 = "android.lens",
    [ANDROID_LENS_INFO]            = "android.lens.info",
    [ANDROID_NOISE_REDUCTION]      = "android.noiseReduction",
    [ANDROID_QUIRKS]               = "android.quirks",
    [ANDROID_REQUEST]              = "android.request",
    [ANDROID_SCALER]               = "android.scaler",
    [ANDROID_SENSOR]               = "android.sensor",
    [ANDROID_SENSOR_INFO]          = "android.sensor.info",
    [ANDROID_SHADING]              = "android.shading",
    [ANDROID_STATISTICS]           = "android.statistics",
    [ANDROID_STATISTICS_INFO]      = "android.statistics.info",
    [ANDROID_TONEMAP]              = "android.tonemap",
    [ANDROID_LED]                  = "android.led",
    [ANDROID_INFO]                 = "android.info",
    [ANDROID_BLACK_LEVEL]          = "android.blackLevel",
};

        对于每个Section端而言,其都占据一个索引区域section_bounds,比如ANDROID_CONTROL Section他所代表的control区域是从ANDROID_CONTROL_START到ANDROID_CONTROL_END之间,且每个Section所拥有的Index范围理论最大可到(1 << 16)大小,完全可以满足统一Section下不同的控制参数的维护。

以ANDROID_CONTROL为列,他的Section index = 1,即对应的section index区间可到(1<<16,2<<16),但一般以实际section中维护的tag的数量来结束,即ANDROID_CONTROL_END决定最终的section index区间。对于每一个section,其下具备不同数量的tag,这个tag是一个指定section下的index值,通过该值来维护一个tag所在的数据区域,此外每个tag都有相应的string  name,在camera_metadata_tag_info.c通过struct tag_info_t来维护一个tag的相关属性:

typedef struct tag_info {
    const char *tag_name;
    uint8_t     tag_type;
} tag_info_t;
其中tag_name为对应section下不同tag的name值 ,tag_type指定了这个tag所维护的数据类型,包括如下:

enum {
    // Unsigned 8-bit integer (uint8_t)
    TYPE_BYTE = 0,
    // Signed 32-bit integer (int32_t)
    TYPE_INT32 = 1,
    // 32-bit float (float)
    TYPE_FLOAT = 2,
    // Signed 64-bit integer (int64_t)
    TYPE_INT64 = 3,
    // 64-bit float (double)
    TYPE_DOUBLE = 4,
    // A 64-bit fraction (camera_metadata_rational_t)
    TYPE_RATIONAL = 5,
    // Number of type fields
    NUM_TYPES
};
对每一个section所拥有的tag_info信息,通过全局结构体tag_info_t *tag_info[ANDROID_SECTION_COUNT] 来定义。

下图是对整个Camera Metadata对不同section以及相应section下不同tag的布局图,下图以最常见的android.control Section为例进行了描述:





2. CameraMetadata通过camera_metadata来维护数据信息

      假设现在存在一个CameraMetadata对象,那么他是如何将一个tag标记的参数维护起来的呢?

CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
        mLocked(false)
{
    mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
}
camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
                                            size_t data_capacity) {
    if (entry_capacity == 0) return NULL;

    size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
                                                          data_capacity);
    void *buffer = malloc(memory_needed);
    return place_camera_metadata(buffer, memory_needed,
                                 entry_capacity,
                                 data_capacity);
}
一个CameraMetadata数据内存块中组成的最小基本单元是struct camera_metadata_buffer_entry,总的entry数目等信息需要 struct  camera_metadata_t来维护

struct camera_metadata {
    size_t                   size;
    uint32_t                 version;
    uint32_t                 flags;
    size_t                   entry_count;//当前实际的entry数目
    size_t                   entry_capacity;//entry最大可以存储的数目
    uptrdiff_t               entries_start; // Offset from camera_metadata
    size_t                   data_count;//当前占据的数据空间
    size_t                   data_capacity;//最大可操作的数据容量
    uptrdiff_t               data_start; // Offset from camera_metadata,大容量数据存储的起始地址
    void                    *user; // User set pointer, not copied with buffer
    uint8_t                  reserved[0];
};

对于每一个entry主要记录他的所代表的TAG,以及这个TAG的需要存储的数据类型,此外还需要记录这个entry是否是需要一个union offset来表示他当前数据量过大时的数据存储位置,

typedef struct camera_metadata_buffer_entry {
    uint32_t tag;//表示当时这个entry代表的tag值,即上文提到的section中不同的tag index值
    size_t   count;
    union {
        size_t  offset;
        uint8_t value[4];
    } data;//如果存储的数据量不大于4则直接存储。否则需要指点一个offset来表示便宜
    uint8_t  type;//维护的数据类型
    uint8_t  reserved[3];
} camera_metadata_buffer_entry_t;


3. update更新并建立参数

CameraMetadata支持不同类型的数据更新或者保存到camera_metadata_t中tag所在的entry当中去,以一个更新单字节的数据为例,data_count指定了数据的个数,而tag指定了要更新的entry。

status_t CameraMetadata::update(uint32_t tag,
        const uint8_t *data, size_t data_count) {
    status_t res;
    if (mLocked) {
        ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
        return INVALID_OPERATION;
    }
    if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
        return res;
    }
    return updateImpl(tag, (const void*)data, data_count);
}
首先是通过checkType,主要是通过tag找到get_camera_metadata_tag_type其所应当支持的tag_type( 因为具体的TAG是已经通过camera_metadata_tag_info.c源文件中的tag_info这个表指定了其应该具备的tag_type),比较两者是否一致,一致后才允许后续的操作,如这里需要TYPE_BYTE一致。

const char *get_camera_metadata_tag_name(uint32_t tag) {
    uint32_t tag_section = tag >> 16;
    if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
        return vendor_tag_ops->get_tag_name(
            vendor_tag_ops,
            tag);
    }
    if (tag_section >= ANDROID_SECTION_COUNT ||
        tag >= camera_metadata_section_bounds[tag_section][1] ) {
        return NULL;
    }
    uint32_t tag_index = tag & 0xFFFF;//取tag在section中的index,低16位
    return tag_info[tag_section][tag_index].tag_name;//定位section然后再说tag
}

int get_camera_metadata_tag_type(uint32_t tag) {
    uint32_t tag_section = tag >> 16;
    if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
        return vendor_tag_ops->get_tag_type(
            vendor_tag_ops,
            tag);
    }
    if (tag_section >= ANDROID_SECTION_COUNT ||
            tag >= camera_metadata_section_bounds[tag_section][1] ) {
        return -1;
    }
    uint32_t tag_index = tag & 0xFFFF;
    return tag_info[tag_section][tag_index].tag_type;
}
分别是通过tag取货section id即tag>>16,就定位到所属的section tag_info_t[],再通过在在该section中定位tag index一般是tag&0xFFFF的低16位为在该tag在section中的偏移值,进而找到tag自身的struct tag_info_t.

updataImpl函数主要是讲所有要写入的数据进行update操作。

status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
        size_t data_count) {
    status_t res;
    if (mLocked) {
        ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
        return INVALID_OPERATION;
    }
    int type = get_camera_metadata_tag_type(tag);
    if (type == -1) {
        ALOGE("%s: Tag %d not found", __FUNCTION__, tag);
        return BAD_VALUE;
    }
    size_t data_size = calculate_camera_metadata_entry_data_size(type,
            data_count);

    res = resizeIfNeeded(1, data_size);//新建camera_metadata_t

    if (res == OK) {
        camera_metadata_entry_t entry;
        res = find_camera_metadata_entry(mBuffer, tag, &entry);
        if (res == NAME_NOT_FOUND) {
            res = add_camera_metadata_entry(mBuffer,
                    tag, data, data_count);//将当前新的tag以及数据加入到camera_metadata_t
        } else if (res == OK) {
            res = update_camera_metadata_entry(mBuffer,
                    entry.index, data, data_count, NULL);
        }
    }

    if (res != OK) {
        ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)",
                __FUNCTION__, get_camera_metadata_section_name(tag),
                get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
    }

    IF_ALOGV() {
        ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) !=
                 OK,

                 "%s: Failed to validate metadata structure after update %p",
                 __FUNCTION__, mBuffer);
    }

    return res;
}
主要分为以下几个过程:

a.通过tag_type存储的数据类型,由calculate_camera_metadata_entry_data_size计算要写入的entry中的数据量。

b. resizeIfNeeded通过已有entry的数量等,增加entry_capacity,或者重建整个camera_metadata_t,为后续增加数据创建内存空间基础。

c. 通过find_camera_metadata_entry获取一个entry的入口camera_metadata_entry_t,如果存在这个tag对应的entry,则将camera_metadata_buffer_entry_t的属性信息转为camera_metadata_entry_t。

typedef struct camera_metadata_entry {
    size_t   index;//在当前的entry排序中,其所在的index值
    uint32_t tag;
    uint8_t  type;
    size_t   count;
    union {
        uint8_t *u8;
        int32_t *i32;
        float   *f;
        int64_t *i64;
        double  *d;
        camera_metadata_rational_t *r;
    } data;//针对不同数据类型,u8表示数据存储的入口地址,不大于4字节即为value[4].
} camera_metadata_entry_t;
d .add_camera_metadata_entry完成全新的entry更新与写入,即这个TAG目前不存在于这个camera_metadata_t中;update_camera_metadata_entry则是直接完成数据的更新。

mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);

4. Java层中CameraMetadata.java和CameraMetadataNative.java

下面以API2中java层中设置AF的工作模式为例,来说明这个参数设置的过程:

mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler);

其中CONTROL_AF_MODE定义在CaptureRequest,java中如下以一个Key的形式存在:

    public static final Key<Integer> CONTROL_AF_MODE =
            new Key<Integer>("android.control.afMode", int.class);

        public Key(String name, Class<T> type) {
            mKey = new CameraMetadataNative.Key<T>(name, type);
        }
在CameraMetadataNative.java中Key的构造

 public Key(String name, Class<T> type) {
            if (name == null) {
                throw new NullPointerException("Key needs a valid name");
            } else if (type == null) {
                throw new NullPointerException("Type needs to be non-null");
            }
            mName = name;
            mType = type;
            mTypeReference = TypeReference.createSpecializedTypeReference(type);
            mHash = mName.hashCode() ^ mTypeReference.hashCode();
        }


其中CONTROL_AF_MODE_CONTINUOUS_PICTURE定义在 CameraMetadata. java中

    public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4;
逐一定位set的入口:

a. mPreviewBuilder是CaptureRequest.java的build类,其会构建一个CaptureRequest

        public Builder(CameraMetadataNative template) {
            mRequest = new CaptureRequest(template);
        }

    private CaptureRequest() {
        mSettings = new CameraMetadataNative();
        mSurfaceSet = new HashSet<Surface>();
    }
mSetting建立的是一个CameraMetadataNative对象,主要用于和Native层进行接口交互,构造如下

    public CameraMetadataNative() {
        super();
        mMetadataPtr = nativeAllocate();
        if (mMetadataPtr == 0) {
            throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
        }
    }


b. CaptureRequest.Build.set()

        public <T> void set(Key<T> key, T value) {
            mRequest.mSettings.set(key, value);
        }
    public <T> void set(CaptureRequest.Key<T> key, T value) {
        set(key.getNativeKey(), value);
    }
考虑到CaptureRequest extend CameraMetadata,则 CaptureRequest.java中getNativeKey

        public CameraMetadataNative.Key<T> getNativeKey() {
            return mKey;
        }
mKey即为之前构造的CameraMetadataNative.Key.

    public <T> void set(Key<T> key, T value) {
        SetCommand s = sSetCommandMap.get(key);
        if (s != null) {
            s.setValue(this, value);
            return;
        }
        setBase(key, value);
    }
    private <T> void setBase(Key<T> key, T value) {
        int tag = key.getTag();

        if (value == null) {
            // Erase the entry
            writeValues(tag, /*src*/null);
            return;
        } // else update the entry to a new value

        Marshaler<T> marshaler = getMarshalerForKey(key);
        int size = marshaler.calculateMarshalSize(value);

        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
        byte[] values = new byte[size];

        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
        marshaler.marshal(value, buffer);

        writeValues(tag, values);
    }

首先来看key.getTag()函数的实现,他是将这个key交由Native层后转为一个真正的在Java层中的tag值:

        public final int getTag() {
            if (!mHasTag) {
                mTag = CameraMetadataNative.getTag(mName);
                mHasTag = true;
            }
            return mTag;
        }
    public static int getTag(String key) {
        return nativeGetTagFromKey(key);
    }
是将Java层的String交由Native来转为一个Java层的tag值。

再来看writeValues的实现,同样调用的是一个native接口,很好的阐明了CameraMetadataNative的意思:

    public void writeValues(int tag, byte[] src) {
        nativeWriteValues(tag, src);
    }

相关native层的实现在下一小节说明。

5. Native层的CameraMetadata结构完成camera参数的传递

    在描述万了CameraMetadata数据的相关操作之后,可明确的一点是SECTION下的TAG是操作他的核心所在。

这里先说明一个在API1 Camera2Client 参数传递的过程,他采用的逻辑是还是在Java层预留了setParameters接口,只是当Parameter在设置时比起CameraClient而言,他是将这个Parameter根据不同的TAG形式直接绑定到CameraMetadata mPreviewRequest/mRecordRequest/mCaptureRequest中,这些数据会由Capture_Request转为camera3_capture_request中的camera_metadata_t settings完成参数从Java到native到HAL3的传递。

但是在Camera API2下,不再需要那么复杂的转换过程,在Java层中直接对参数进行设置并将其封装到Capture_Request即可,即参数控制由Java层来完成。这也体现了API2中Request和Result在APP中就大量存在的原因。对此为了和Framework Native层相关TAG数据的统一,在Java层中大量出现的参数设置是通过Section Tag的name来交由Native完成转换生成在Java层的TAG。

对于第三小节中提到的native层的实现,其对应的实现函数位于android_hardware_camera2_CameraMetadata.c中,如CameraMetadata_getTagFromKey是实现将一个Java层的string转为一个tag的值,他的主要原理如下:根据传入的key string值本质是由一个字符串组成的如上文中提到的android.control.mode。对比最初不同的Section name就可以发现前面两个x.y的字符串就是代表是Section name.而后面mode即是在该section下的tag数值,所以通过对这个string的分析可知,就可以定位他的section以及tag值。这样返回到Java层的就是key相应的tag值了。

如果要写数据,那么在native同样需要一个CameraMetadata对象,这里是在Java构造CameraMetadataNative时实现的,调用的native接口是nativeAllocate():

static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
    ALOGV("%s", __FUNCTION__);

    return reinterpret_cast<jlong>(new CameraMetadata());
}
最终可以明确的是CameraMetadata相关的参数是被Java层来set/get,但本质是在native层进行了实现,后续如果相关控制参数是被打包到CaptureRequest中时传入到native时即操作的还是native中的 CameraMetadata。

















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

Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互 的相关文章

  • 论文阅读笔记之——《Toward Convolutional Blind Denoising of Real Photographs》及基于pytorch的CBDNet的复现

    本文是CBDNet xff08 convolutional blind denoising network xff09 的阅读笔记 本博文分为两块 xff0c 一块是阅读笔记 xff0c 一块是本人对CBDNet的实验记录 论文链接 xff
  • C++和Windows平台的一些书籍

    从2010年学习编程以来 xff0c 到现在有差不多3年时间了 xff0c 过的真快啊 目前在深圳工作 xff0c 主要使用的是C 43 43 语言 xff0c 那么我就说说C 43 43 和Windows平台的书籍吧 1 C primer
  • 程序员提升之排查bug的能力

    不知不觉工作已经快一年了 xff0c 从开始工作以后 xff0c 跟着大神们学习了很多 xff0c 从一个小白慢慢的成长起来 xff0c 从我目前的经验来看 xff0c 我要感谢bug xff0c 这里的bug并不单单是指bug xff0c
  • CMakeList编译报错ninja: error: missing and no known rule to make it解决方法

    Android NDK开发引用第三方库CMakeList txt编译报错 xff1a ninja error 39 XXX NDKApplication app src main cpp src main cpp armeabi v7a l
  • 关于Java学习的心得体会

    大一的第二学期 xff0c 满心欢喜的期待着自己的专业课 xff0c 上个学期C语言老师和我们说C语言只是入门 xff0c 我们还学得如此吃力 心中已经能够想到本学期的心酸了 xff0c 果不其然 xff0c 老师说本学期不学C 43 43
  • 项目管理第十三章项目相关方管理

    项目管理第十三章项目相关方管理 项目相关方管理 xff1a 包括识别能够影响项目或会受项目影响的人员 团体或组织 xff0c 分析相关方对项目的期望和影响 xff0c 制定合适的管理策略来有效调动相关方参与项目决策和执行 其过程包括 xff
  • Git3.创建版本库(Git仓库)&提交更新到仓库

    创建版本库 Git仓库 什么是版本库呢 xff1f 版本库又名仓库 xff0c 英文名repository xff0c 你可以简单理解成一个目录 xff0c 这个目录里面的所有文件都可以被Git管理起来 xff0c 每个文件的修改 删除 x
  • ssh 登录时常出现的几种错误以及解决方法(Linux)

    前言 ssh是Linux系统中常用的远程登陆的命令 xff0c 有的时候我们通过xshell等远程连接软件使用ssh去登陆远程的服务器的时候 xff0c 会遇到一些问题 下面是关于ssh 远程登陆的问题解决方法的总结 xff08 更新中 x
  • vins-mono(4)重定位以及全局优化

    vins的重定位模块主要包含回环检测 回环候选帧之间的特征匹配 紧耦合重定位三个部分 a 回环检测 采用BRIEF描述子的DBOW2词袋进行闭环检测 对新来的关键帧重新检测500个角点进行闭环检测同时对所有角点进行BRIEF描述 然后计算当
  • STM32CubeMX的下载以及安装

    第一步 xff1a 安装应用软件 下载STM32CubeMX软件并安装 xff1b 第二步 xff1a 安装MCU固件包 在线安装用户所选的STM32微控制器对应的MCU固件包 具体操作步骤如下 xff1a 软件的下载地址 xff1a ht
  • MDK-ARM软件的下载以及资源分享

    1 软件的基本特点 xff1a 支持源代码的编辑 编译 程序的下载和调试等多种功能 编译器和器件支持包分离 xff0c 可根据需求安装 xff0c 减少软件的大小 提供多种中间件 xff0c 如RTOS和GUI等 xff0c 提高开发效率
  • 关于Win10家庭版修改用户名的问题

    之前电脑注册的时候命名为中文 xff0c 但在装很多软件时就会出现很多奇奇怪怪的问题 xff0c 查了之后发现可能是因为系统的中文名造成的 xff0c 因此在之后可能就需要修改 之前在网上找到比较常见的两种方法都不适用于Win10家庭版 方
  • ROS 开源智能小车 TurtleBot3 Waffle pi 深度视觉版简介 (可实现地图搭建、自主导航、自主避障、深度视觉算法研究)

    ROS机器人操作系统为什么现在火爆全球 xff1f ROS xff08 Robot Operating System xff0c 机器人操作系统 xff09 是目前世界上更主流更多人使用的机器人开源操作系统 它可以提供操作系统应有的服务 x
  • c/c++输出保留指定位小数(输出精度控制)

    c 43 43 C 43 43 的精度控制需要头文件 span class token macro property span class token directive keyword include span span class to
  • 以连接mysql为例:六种流行的语言比较——C、C++、python、Java、php、C#

    本文是六种语言连接mysql数据库的代码展示 xff0c 在LZ尝试的过程中 xff0c 无论是语言环境搭建 mysql依赖库的导入还是代码的风格 xff0c 各种语言都各有千秋 接下来 xff0c 我们就让这些语言一一登场吧 Java x
  • 熊迈摄像头SDK二次开发:登录、截屏、录像等功能

    对雄迈摄像头二次开发 xff0c 因为雄迈给的资料比较混乱 xff0c 没有找到可用的sdk手册 xff0c 所以一开始想用OpenCV 43 qt 但是在Windows环境下 xff0c opencv摄像头读取卡顿严重 xff0c 而且o
  • ros学习(2-2):ros节点创建(python)

    参考 Python创建简单的ROS节点 xff1a 用Python实现ROS节点 xff08 这里也说明了用Python写ROS功能包也是需要CMakelists txt的 xff09 https blog csdn net sinat 1
  • Ubuntu下使用眼动仪Tobii Eye Tracker 4C

    01 安装 amp 使用 安装 使用过程参考 xff1a https github com Eitol tobii eye tracker linux installer 下面过程主要是github中自述文件的翻译 下载 在新终端 xff0
  • 深度学习之卷积

    01 卷积 卷积是指在滑动中提取特征的过程 xff0c 可以形象地理解为用放大镜把每步都放大并且拍下来 xff0c 再把拍下来的图片拼接成一个新的大图片的过程 2D卷积是一个相当简单的操作 xff1a 我们先从一个小小的权重矩阵 xff0c
  • Ubuntu 18.04 安装网卡驱动(有线连接)

    之前一直用小米的驱动 xff0c 但是感觉太慢了 xff0c 所以还是决定使用有线连接 但是之前进入Linux系统后 xff0c 有线连接没有 xff0c 所以需要安装驱动 01 查询网卡类型 xff08 Windows xff09 进入w

随机推荐