草庐IT

MMDetection3d对KITT数据集的训练与评估介绍

Clichong 2023-06-03 原文

如有错误,恳请指出。


在之后的时间内,可能会学习与点云相关的知识,进一步学习基于点云的3D目标检测。然后,为了快速入门这个领域,想使用mmdetection3d开源算法库来尝试训练一些经典的3d目标检测模型,比如:SECOND,PointPillars,3D-SSD等等。之后重点是详细介绍KITTI数据集的一系列评估标准。

文章目录

1. mmdet3d安装过程

其中,推荐安装python=3.7的虚拟环境,因为在kitti数据集可视化过程中需要使用到mayavi包,而这个包的vtk依赖不支持python 3.8版本。

# 创建虚拟环境
conda create --name openmmlab python=3.7 -y
conda activate openmmlab

# 安装深度学习框架
# Linux
conda install pytorch==1.11.0 torchvision==0.12.0 torchaudio==0.11.0 cudatoolkit=11.3
# Windows
pip install torch==1.11.0+cu102 torchvision==0.12.0+cu102 torchaudio==0.11.0 --extra-index-url https://download.pytorch.org/whl/cu102

# 安装mmdet3d开源算法库
pip install openmim
mim install mmcv-full
mim install mmdet
mim install mmsegmentation
git clone https://github.com/open-mmlab/mmdetection3d.git
cd mmdetection3d
pip install -e .

ps:在linux可能出现的问题不会有太多,但是如果是window中安装,出现pycocotools无法正常安装的问题,原因是没有vc++的编译器,安装一个visual studio 2019的专业版就可以正常安装了mmdet了,而mmdet的依赖就是pycocotools。

这个问题的详细解决方法见:error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools“


2. KITTI数据集准备

在官网下载对应的数据:http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d
安装官方文档对数据进行组织:https://mmdetection3d.readthedocs.io/zh_CN/latest/datasets/kitti_det.html

ps:官方提供的数据切分指令如果wget无法正常下载访问,可以在/etc/hosts中自行添加网页的IP地址(IP地址的查询服务链接:https://www.ip138.com/),例如:

wget -c  https://raw.githubusercontent.com/traveller59/second.pytorch/master/second/data/ImageSets/test.txt --no-check-certificate --content-disposition -O ./data/kitti/ImageSets/test.txt

这个问题的详细解决方法,我也用博客贴出:Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|0.0.0.0|:443… failed

处理后的文件夹结构应该如下:

kitti
├── ImageSets
│   ├── test.txt
│   ├── train.txt
│   ├── trainval.txt
│   ├── val.txt
├── testing
│   ├── calib
│   ├── image_2
│   ├── velodyne
│   ├── velodyne_reduced
├── training
│   ├── calib
│   ├── image_2
│   ├── label_2
│   ├── velodyne
│   ├── velodyne_reduced
│   ├── planes (optional)
├── kitti_gt_database
│   ├── xxxxx.bin
├── kitti_infos_train.pkl
├── kitti_infos_val.pkl
├── kitti_dbinfos_train.pkl
├── kitti_infos_test.pkl
├── kitti_infos_trainval.pkl
├── kitti_infos_train_mono3d.coco.json
├── kitti_infos_trainval_mono3d.coco.json
├── kitti_infos_test_mono3d.coco.json
├── kitti_infos_val_mono3d.coco.json

这里我不太理解mmdet3d转换出来的这些.pkl与.coco.json文件,所以下面写了个测试代码来对其进行详细了解。

2.1 了解 .coco.json 文件

对于 .coco.json 后缀的文件,可以通过两种方式对其进行读取,如下所示:

# 方法1:
json_path = r'../data/kitti/kitti_infos_train_mono3d.coco.json'
with open(json_path, 'r') as fr:
    json_file = json.load(fr)

# 方法2:
cocojson_path = '../data/kitti/kitti_infos_train_mono3d.coco.json'
cocojson_file = mmcv.load(cocojson_path)

ps:如果由于某些原因导致json文件的内容被情况,也就是变成了一个空的.coco.json文件,这时候使用以上的两种方法读取数据是会报错的,这一点需要尤其注意。(在测试过程中我就是出现了这个题,还排查了许久的原因)

其中,查看kitti_infos_train_mono3d.coco.jsonkitti_infos_test_mono3d.coco.json内容如下所示,有固定字段组成的字典形式,由annotation、images、categories组成,其中训练集有标注信息,而验证集没有标注信息,其字段信息如下所示:

其中的images又是由多个字典组成,如下所示:

不过在训练的时候,我看见配置文件中其实使用的pkl文件,所以这里生成的.coco.json文件更多的可能是使用一些2D的经典目标检测算法来训练kitti数据集的。也就是说,生成的.coco.json文件,使得我们可以使用mmdetection的开源算法库(检测2d的算法)来训练kitti数据集。

2.2 了解 .pkl 文件

使用一般的手段是不能正常读取.pkl文件的,np.fromfile 与 json.load 都不能正常读取,因为这是mmdet中独特的数据存储格式。对于网上没有这种文件的详细介绍,此时就可以转换思路通过如何生成 .pkl 文件入手,知道了其如何生成就应该知道其存储的内容是什么。

在代码层面,首先在 tools/create_data.py 中的 kitti_data_pre 函数中,作为数据处理的入口。之后可以发现,代码中是通过 mmcv.dump 与 mmcv.load 来进行pkl格式数据与.coco.json格式数据的写入与导出,但是数据本身就是一个字典形式。具体的构造在 get_kitti_image_info 函数中,函数的相关注释如下:

KITTI annotation format version 2:
{
    [optional]points: [N, 3+] point cloud
    [optional, for kitti]image: {
        image_idx: ...
        image_path: ...
        image_shape: ...
    }
    point_cloud: {
        num_features: 4
        velodyne_path: ...
    }
    [optional, for kitti]calib: {
        R0_rect: ...
        Tr_velo_to_cam: ...
        P2: ...
    }
    annos: {
        location: [num_gt, 3] array
        dimensions: [num_gt, 3] array
        rotation_y: [num_gt] angle array
        name: [num_gt] ground truth name array
        [optional]difficulty: kitti difficulty
        [optional]group_ids: used for multi-part object
    }
}

这里,直接利用 mmcv.load 来读取 kitti_test.pkl 数据,pkl_file = mmcv.load(file=pkl_path) ,其内容如下所示:

可以发现,其作为一个列表元素,每个训练数据都被构建成了一个字典。每个字典由4大部分组成:inage、point_cloud、calib、annos。其与注释内容是相符合的,其中可以使用 mmcv.track_iter_progress 可以按顺序对字典进行提取:for info in mmcv.track_iter_progress(kitti_infos)

其中,对于这4个部分,官方资料中有解释到:https://mmdetection3d.readthedocs.io/zh_CN/latest/datasets/kitti_det.html

可以发现,其实这里的annos就是kitti中的label文件的数据。而对于label_2的介绍,可以参考https://blog.csdn.net/qq_37534947/article/details/106628308,label文件是kitti中object的标签和评估数据,以“000001.txt”文件为例,包含样式如下:

每一行代表一个object,每一行都有16列分别表示不同的含义,具体如下:

  • 第1列(字符串):代表物体类别(type)
    总共有9类,分别是:Car、Van、Truck、Pedestrian、Person_sitting、Cyclist、Tram、Misc、DontCare。其中DontCare标签表示该区域没有被标注,比如由于目标物体距离激光雷达太远。为了防止在评估过程中(主要是计算precision),将本来是目标物体但是因为某些原因而没有标注的区域统计为假阳性(false positives),评估脚本会自动忽略DontCare区域的预测结果。
  • 第2列(浮点数):代表物体是否被截断(truncated)
    数值在0(非截断)到1(截断)之间浮动,数字表示指离开图像边界对象的程度。
  • 第3列(整数):代表物体是否被遮挡(occluded)
    整数0、1、2、3分别表示被遮挡的程度。
  • 第4列(弧度数):物体的观察角度(alpha)
    取值范围为:-pi ~ pi(单位:rad),它表示在相机坐标系下,以相机原点为中心,相机原点到物体中心的连线为半径,将物体绕相机y轴旋转至相机z轴,此时物体方向与相机x轴的夹角,如图1所示。
  • 第5~8列(浮点数):物体的2D边界框大小(bbox)
    四个数分别是xmin、ymin、xmax、ymax(单位:pixel),表示2维边界框的左上角和右下角的坐标。
  • 第9~11列(浮点数):3D物体的尺寸(dimensions)分别是高、宽、长(单位:米)
  • 第12-14列(整数):3D物体的位置(location)分别是x、y、z(单位:米),特别注意的是,这里的xyz是在相机坐标系下3D物体的中心点位置。
  • 第15列(弧度数):3D物体的空间方向(rotation_y)取值范围为:-pi ~ pi(单位:rad),它表示,在照相机坐标系下,物体的全局方向角(物体前进方向与相机坐标系x轴的夹角)
  • 第16列(整数):检测的置信度(score)要特别注意的是,这个数据只在测试集的数据中有。

2.3 .bin文件、.pkl文件、.coco.json文件的查看代码

如何查看KITTI数据集涉及的这些.bin文件,以及mmdetection3d统一的.pkl与.coco.json格式,以下我写了个代码已实现对这些文件的查看。

参考代码:

import numpy as np
import os
import json

import mmcv

# os.chdir(path='')

root = os.getcwd()
print(root)

bin_path = '../data/kitti/kitti_gt_database/1000_Car_0.bin'
pkl_path = '../data/kitti/kitti_infos_train.pkl'
# 原来是 test_mono3d.coco.json由于名称的问题,读取不了数据
json_path = r'../data/kitti/test.json'
os.path.exists(bin_path), "{} is not exists".format(bin_path)
os.path.exists(json_path), "{} is not exists".format(json_path)

bin_file = np.fromfile(file=bin_path, dtype=np.float32, count=-1)
pkl_file = np.fromfile(file=pkl_path, dtype=np.float32, count=-1)
print(bin_file.shape)
print(pkl_file.shape)

# with open(json_path, 'r') as fr:
#     json_file = json.load(fr)
# print(json_file)

pkl_path = '../data/kitti/kitti_infos_train.pkl'
kitti_infos = mmcv.load(file=pkl_path)
# print(pkl_file)

json_path = r'../data/kitti/kitti_infos_train_mono3d.coco.json'
with open(json_path, 'r') as fr:
    json_train_file = json.load(fr)
# print(json_file)

cocojson_path = '../data/kitti/kitti_infos_test_mono3d.coco.json'
json_test_file = mmcv.load(cocojson_path)

补充:使用 mmcv.load 不仅仅可以读取,coco.json和.pkl文件,其一共支持5种格式的数据格式。如下所示:

file_handlers = {
    'json': JsonHandler(),
    'yaml': YamlHandler(),
    'yml': YamlHandler(),
    'pickle': PickleHandler(),
    'pkl': PickleHandler()
}

3. KITTI数据集训练

如果你在单个机器上启动多个任务,比如,在具有8块显卡的机器上进行2个4块显卡训练的任务,你需要为每个任务指定不同的端口(默认为29500)以避免通信冲突。

如果你使用 dist_train.sh 启动训练任务,可以在命令中设置端口:

CUDA_VISIBLE_DEVICES=0,1,2,3 PORT=29500 ./tools/dist_train.sh ${CONFIG_FILE} 4
CUDA_VISIBLE_DEVICES=4,5,6,7 PORT=29501 ./tools/dist_train.sh ${CONFIG_FILE} 4

端口的设置还有另外的两种方法:

# 方法1:通过 --options 设置端口
CUDA_VISIBLE_DEVICES=0,1,2,3 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config1.py ${WORK_DIR} --options 'dist_params.port=29500'
CUDA_VISIBLE_DEVICES=4,5,6,7 GPUS=4 ./tools/slurm_train.sh ${PARTITION} ${JOB_NAME} config2.py ${WORK_DIR} --options 'dist_params.port=29501'

# 方法2:通过修改配置文件(来设置不同的通信端口
dist_params = dict(backend='nccl', port=29500)
dist_params = dict(backend='nccl', port=29501)

详细的训练过程这里就不贴出来的,和mmdetection、mmfewshot等的训练过程是一样的。详细可以参考:MMOpenLab使用专栏

经过测试,出现的使用问题:

1)训练MVXNET:弹出非法访问的问题(解决方法是降低学习率,以实现单卡运行的学习率配置)
2)训练Point RCNN:占用显存不断增加,最终溢出(官方还没有修复这个问题)

成功测试的算法:SECOND、3D-SSD、PointPillars、Part-A2、SASSD


4. KITTI数据集评估标准

以PointPillars为例,训练完之后会有一个评估结果(以下结果是截取了Car这个类的结果,还有另外两个类的结果没有放上来)

----------- AP11 Results ------------
Car AP11@0.70, 0.70, 0.70:
bbox AP11:90.5385, 89.3699, 86.2703
bev  AP11:89.6290, 86.8373, 79.6047
3d   AP11:85.9857, 76.4022, 73.5934
aos  AP11:90.38, 88.89, 85.46
Car AP11@0.70, 0.50, 0.50:
bbox AP11:90.5385, 89.3699, 86.2703
bev  AP11:90.6688, 89.9088, 89.0676
3d   AP11:90.6654, 89.8380, 88.8712
aos  AP11:90.38, 88.89, 85.46

Overall AP11@easy, moderate, hard:
bbox AP11:79.9252, 74.4630, 71.3123
bev  AP11:77.1720, 68.2989, 63.5469
3d   AP11:73.1700, 62.1137, 58.4967
aos  AP11:74.50, 68.65, 65.49

----------- AP40 Results ------------
Car AP40@0.70, 0.70, 0.70:
bbox AP40:95.6966, 92.1547, 87.4279
bev  AP40:92.4469, 88.1739, 83.6487
3d   AP40:87.8139, 76.5133, 73.3604
aos  AP40:95.51, 91.61, 86.58
Car AP40@0.70, 0.50, 0.50:
bbox AP40:95.6966, 92.1547, 87.4279
bev  AP40:95.9824, 94.8116, 91.7013
3d   AP40:95.9314, 94.6114, 90.0106
aos  AP40:95.51, 91.61, 86.58

Overall AP40@easy, moderate, hard:
bbox AP40:82.7911, 75.4843, 71.5740
bev  AP40:78.7391, 68.7362, 64.2875
3d   AP40:74.1775, 61.6529, 57.5156
aos  AP40:76.47, 68.73, 64.88

对于上述的结果,下面分别对特定的名词进行解释说明:

4.1 bbox、bev、3d、aos

深度学习算法的检测指标通常由bbox、bev、3d、aos四个检测指标,其含义分别如下所示:

  • bbox:2D检测框的准确率
  • bev:BEV视图下检测框的准确率
  • 3d:3D检测框的准确率
  • aos:检测目标旋转角度的准确率

4.2 AP11与AP40

  • AP11:表示11点插值平均精度,在kitti 3D中R11={0,0.1,0.2,……,1},是等间距的recall level
  • AP40:表示40点插值平均精度,将R11修改为R40={1/40,2/40,3/40,……,1},同样是等间距的recall level

ps:论文《Disentangling Monocular 3D Object Detection》证明AP11是不准确的,因为当模型可以提供一个精度极小,仅仅是>0的一个单一目标,此时R=0时的精度即为1,那么AP11的平均精度即为1/11,这个精度已经超过了很多的方法,所以是不合理的。所以后续修改为AP40。

4.3 Car AP11@0.70, 0.70, 0.70与Car AP11@0.70, 0.50, 0.50

  • AP11@0.70, 0.70, 0.70分别代表bbox,bev,3d在0.70阈值下的平均精度
  • AP11@0.70, 0.50, 0.50分别代表bbox,bev,3d在0.70,0.50,0.50不同阈值下的平均精度

这里可以发现,评估bbox只有0.70这个阈值,所以可以发现bbox的两行数据都是一样的,而对于bev与3d来说,0.50的阈值比0.70的阈值要宽松,所以第二组的结果(阈值0.50)一般要比第一组的结果(阈值0.70)要高。

4.4 bbox AP11:90.5385, 89.3699, 86.2703

无论是bbox,还是bev、3d、aos,每个评价指标在某一个阈值下都会有3组结果,这三组结果分别对应的是easy、moderate和hard下的评估结果。难度越来越大,所以数值也越来越小,所以这三组数值一般是呈递减状态。

4.5 mAP

一般论文中的实验结果都会贴上一个mAP的最终结果,这个结果就是moderate mAP的结果。比如,在刚刚的PointPillars实验结果中,对于car类别的AP11结果如下所示,其中76.4022就是作为car这个类基准排名的主要指标。

Car AP11@0.70, 0.70, 0.70:
3d   AP11:85.9857, 76.4022, 73.5934

而对于全部3个类别的AP11结果如下所示,那么62.1137就是作为3类(3 Class)基准排名的主要指标。

Overall AP11@easy, moderate, hard:
3d   AP11:73.1700, 62.1137, 58.4967

现在,来查看mmdetection3d中PointPillars的结果:

可以发现,其实刚刚我跑出来的结果Class的AP是76.4022,而3 Class的AP是62.137。这个结果和官方跑出来的77.6和64.07差不多。使用以下指令,来测试刚刚训练好的PointPillars的最新模型:

python tools/test.py configs/pointpillars/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class.py \
                     work_dirs/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class/latest.pth \
                     --eval mAP

输出结果:

可以看见,无论是Car=76.4022还是3 Class=62.1137,都与训练时期的验证结果差不多,所以训练期间的验证结果还是可信的。

4.6 aos(Average Orientation Similarity)

aos的名称为平均方向相似性(这里是利用了AP11来进行计算),计算公式如下:

其中,r代表物体检测的召回率。在因变量r下,方向相似性s∈[0,1]被定义为所有预测样本与ground truth余弦距离的归一化

其中D®表示在召回率r下所有预测为正样本的集合,∆θ(i) 表示检出物体i的预测角度与ground truth的差。为了惩罚多个检出匹配到同一个ground truth,如果检出i已经匹配到ground truth设置δi = 1,否则δi = 0。

简要分析:我们希望aos的值越大越好,越大也就说明预测角度与ground truth的值越相似,那么这个是如何实现的呢。在余弦函数中,如果预测角度与ground turth的值越接近,那么它们差值就越接近0,余弦值是越靠近1,也就是余弦越大,那么整个公式的求和平均也是越大的。而如果预测值与ground truth不像,严重预测错误,那么其差值就会变大,一个比较大的差值在余弦公式中是比较小的,甚至可能是负数,导致1+cos的结果很小,从而使得整体的结果偏小。所以aos公式可以一定程度的判断方向的预测正确性。

4.7 easy、moderate、hard的定义

KITTI数据集中easy、moderate、hard根据标注框是否被遮挡、遮挡程度和框的高度进行定义的,具体数据如下:

  • 简单:最小边界框高度:40像素,最大遮挡级别:完全可见,最大截断:15%
  • 中等:最小边界框高度:25像素,最大遮挡水平:部分遮挡,最大截断:30%
  • 困难:最小边界框高度:25像素,最大遮挡级别:难以看到,最大截断:50%

参考资料:

1. MMDet3d官方文档

2. What is the resuls meaning? #185

3. KITTI数据集3d目标检测的评价的含义

4. 机器学习算法评估指标——3D目标检测

5. 点云感知算法面试知识点(一)

有关MMDetection3d对KITT数据集的训练与评估介绍的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  3. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  4. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  5. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  6. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  7. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  8. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  9. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  10. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

随机推荐