前言

在博客阅读前需要说明,本博文为系列文章,通过阅读文章,您将会学习到如下内容:
  • 使用NVIDIA Transfer Learning Toolkit 工具训练(finetuning)出一个行人检测算法模型;
  • 将训练好的算法模型部署到NVIDIA Jetson TX2上,结合deepstream sdk实现模型对多路(16路)rtsp视频流进行实时的行人检测;

思路的简介(目录)

在我们做一件事之前,需要思考;做事的思路,做事的方式,这样我们才能顺利、高效的完成这件事;

本篇博客:

  • Docker-ce的安装
  • Nvidia-TLT工具的安装
  • 训练数据的准备
  • 模型训练配置文件的调整
  • 算法模型的训练
  • 算法模型的评估
    下一篇博客:
  • 算法模型的purn
  • purn之后模型的再训练
  • 模型导出到tx2
  • 使用deepstream sdk进行多路视屏流的行人检测
接下来我们开始上具体的实验流程和代码;

(一)Docker-ce的安装(参考地址)

由于我们使用的TLT工具是一个镜像环境,因此我们需要预先安装好docker。docker的安装很多博客有介绍了,这里本文就直接照搬aliyun上提供的安装步骤来进行安装:
Ubuntu 14.04/16.04(使用 apt-get 进行安装)
# step 1: 安装必要的一些系统工具
sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# step 2: 安装GPG证书
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# Step 3: 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# Step 4: 更新并安装Docker-CE
sudo apt-get -y update
sudo apt-get -y install docker-ce# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# apt-cache madison docker-ce
#   docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
#   docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)
# sudo apt-get -y install docker-ce=[VERSION]
待完成上述步骤后,可打开终端检查检查一下docker的安装情况,在终端中输入指令 docker 即可(部分信息截图如下):

这一块没有过多的描述,安装好就结束了;

(二)NVIDIA-TLT工具镜像的下载

[ps: 特别提示,安装此工具前您需要将你电脑基本的环境搭建好,cuda+cudnn+nvidia显卡驱动]

镜像的下载需要去NVIDIA NGC官网,NGC官方地址:https://ngc.nvidia.com/catalog/collections
具体步骤如下:
  • 如果您之前有注册过NGC官网的账号,请按照提示登陆上您的账号;若是没有注册,第一次使用,那么需要按照注册步骤注册一个账号,并登陆;
  • 待登陆上您的账号后,我们在collections页面搜索NVIDIA TLT工具(搜索结果如下图);
  • 出现该工具的镜像后,我们点击进去,进去页面后会有关于该镜像的详细信息(页面如下图所示),以及如何download这个镜像的详细步骤(本文将相关步骤描述至图片下方);
  • 安装步骤
    1.打开终端输入如下指令,根据你的网络情况,耐心等待安装即可;

    docker pull nvcr.io/nvidia/tlt-streamanalytics:v2.0_py3
    

    2.下载成功后,启动镜像

    docker run --gpus all -itd -p 8888:8888 nvcr.io/nvidia/tlt-streamanalytics:v2.0_py3 /bin/bash
    
  • 若镜像启动成功,即可成功进入镜像内,如下图所以(下图为后面再次进入镜像时的截图,初始化安装后的启动未截图保存,拿此图作为展示);
到此步骤,我们所需软件环境的安装流程已经搞定,接下来即可开始算法模型的训练了;

(三)训练数据的准备

在本文实验过程中,采用的是NVIDIA的detectnet_v2算法,backbond为resnet18。因此需要按照该算法框架使用的数据格式来进行数据集的准备;
需要注意的是,算法模型的训练需要采用kitti数据集格式的标签文件,因此,我们需要将用于训练的数据标签格式转换至kitti标签格式;并且,模型训练过程不支持输入任意尺度的照片,因此在训练之前照片尺寸以及标签也需要做调整;
开始之前先简单介绍一下用于训练的数据集。该数据集来源于网络(baipiao),数据格式为VOC格式,一张图对应一个xml文件,如下图所示,由于本文为了快速实验和介绍流程,只从数据集中选择了1000张左右的图片用于本文的实验流程;

ok,开始我们的数据预处理流程,数据预处理的流程如下
  • 获取照片的路径,生成照片路径的txt文件: get_path.py
import ossrc_dir = './images'
pic_name = os.listdir(src_dir)
with open('train.txt', 'a') as f:for pic in pic_name:full_path = os.path.join(src_dir, pic)f.writelines(full_path + '\n')
f.close()

生成的train.txt文件内容示例如下所示(可以用相对路径,也可以使用绝对路径):

/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s597.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5402.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5648.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5722.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5365.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5695.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5840.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5384.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5136.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5651.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5882.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5653.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5227.jpg
/home/lxz/data-256/data/tlt-expriments/datasets/test_data/images/s5780.jpg
  • 将数据集转换至yolo标签格式:transfer_voc_label.py(为什么:万一你想用yolo训练呢,哈哈。当然,如果您不需要yolo格式,可自行编写脚本处理xml文件直接转到kitti标签格式)
# coding: utf-8
# author: HXY
"""
标签格式的装换;
"""
import os
from os.path import join
import xml.etree.ElementTree as ETdef convert(size, box):dw = 1./(size[0])dh = 1./(size[1])x = (box[0] + box[1])/2.0 - 1y = (box[2] + box[3])/2.0 - 1w = box[1] - box[0]h = box[3] - box[2]x = x*dww = w*dwy = y*dhh = h*dhreturn (x,y,w,h)def convert_annotation(image_id):classes = ["person"]in_file = open('./xml/%s.xml'%(image_id))out_file = open('./labels/%s.txt'%(image_id), 'w')tree=ET.parse(in_file)root = tree.getroot()size = root.find('size')w = int(size.find('width').text)h = int(size.find('height').text)for obj in root.iter('object'):difficult = obj.find('difficult').textcls = obj.find('name').textif cls not in classes or int(difficult)==1:continuecls_id = classes.index(cls)xmlbox = obj.find('bndbox')b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))bb = convert((w,h), b)out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')def transfer():images_txt = "train.txt"labels_save_path = './labels'if not os.path.exists(labels_save_path):os.mkdir(labels_save_path)with open(images_txt, "r") as f:for file in f.read().splitlines():id = file.split('/')[-1].split('.')[0]convert_annotation(image_id=id)f.close()print("标签格式转换成功....")if __name__ == '__main__':transfer()
  • 将yolo格式的数据标签转换至kitti数据集格式的标签:yolo_txt_to_kitti.py
# coding: utf-8
# author: hxy
"""
代码用于将yolo的txt标签格式转换到kitti的txt标签格式;
"""
import os
import cv2
import time# 将txt中坐标还原到原始照片的坐标
def restore_coordinate(yolo_bbox, image_w, image_h):box_w = float(yolo_bbox[3]) * image_wbox_h = float(yolo_bbox[4]) * image_hx_mid = float(yolo_bbox[1]) * image_w + 1y_mid = float(yolo_bbox[2]) * image_h + 1xmin = int(x_mid - box_w / 2)xmax = int(x_mid + box_w / 2)ymin = int(y_mid - box_h / 2) + 3ymax = int(y_mid + box_h / 2) + 3return [xmin, ymin, xmax, ymax]# 获取照片的labels文件和images文件
def restore_results(images_folder, labels_folder, kitti_labels):labels = os.listdir(labels_folder)for label in labels:name = label.split('.')[0]with open(os.path.join(labels_folder, label), 'r') as f:infos = f.readlines()with open(os.path.join(kitti_labels, name + '.txt'), 'w') as f1:for boxes in infos:info = boxes.strip('\n')label = list(info.split(' '))img = cv2.imread(os.path.join(images_folder, name + '.jpg'))w = img.shape[1]h = img.shape[0]box = restore_coordinate(label, w, h)# # 将转换的坐标值绘制到原始图片上,验证看看转换的坐标值是否ok;# cv2.rectangle(img, (box[0], box[1]), (box[2], box[3]), (0, 255, 255), 2)# cv2.imshow('Transfer_label', img)# if cv2.waitKey(100) & 0XFF == ord('q'):#     breaknew_info = class_name + ' ' + '0.00' + ' ' + '0' + ' ' + '0.00' + ' ' \+ str(box[0]) + ' ' + str(box[1]) + ' ' + str(box[2]) + ' ' + str(box[3]) \+ ' ' + '0.00' + ' ' + '0.00' + ' ' + '0.00' + ' ' + '0.00' + ' ' + '0.00' \+ ' ' + '0.00' + ' ' + '0.00'f1.write(new_info + '\n')f1.close()f.close()if __name__ == '__main__':s = time.time()print('----数据转换开始---')class_name = 'person'restore_results('./images','./labels','./kitti_labels')print('---耗时:{:.2f}s'.format(time.time() - s))print('---数据转换成功---')
这个脚本里面的文件路径解释一下:./images代表的是原始照片的存储文件夹;./labels代表的是yolo格式的标签存储文件夹;./kitti_labels代表的是新转换成功的kitti格式的标签存储文件夹;代码中有部分注释的代码是用于检查标签转换结果可视化的代码;
转换成功后的标签文件如下图所示(上面为kitti格式,下面为yolo格式):

  • 将图片进行resize操作:resize_imgs.py(由于采用的是resnet18作为backbond,并且根据官方文档的描述,说输入图片的尺寸w、h需要为16的倍数,本文用于训练的照片尺寸为600*800的,因此需要调整)
# coding: utf-8
# author: lxz-hxy
"""
用于将照片进行resize以及将对应的标签文件进行相应的变换
"""import os
import cv2def resizeImg(w, h, imgs_floder, save_folder):imgs = os.listdir(imgs_floder)for img in imgs:img_full_path = os.path.join(imgs_floder, img)ori_img = cv2.imread(img_full_path)img_r = cv2.resize(ori_img, (w, h))cv2.imwrite(os.path.join(save_folder, img), img_r)adjust_labels(img,w,h,'./kitti_labels','./resize_kitti_labels',img_r)return 0def adjust_labels(imgName, w, h, ori_labels_folder, new_labels_folder, img):x_scale_ratio = float(w / 600)y_scale_ration = float(h / 800)with open(os.path.join(ori_labels_folder, imgName.split('.')[0] + '.txt'), 'r') as f:infos = f.readlines()with open(os.path.join(new_labels_folder, imgName.split('.')[0] + '.txt'), 'w') as w:for box in infos:info = box.strip('\n')label_info = info.split(' ')xmin = int(label_info[4]) * x_scale_ratioymin = int(label_info[5]) * y_scale_rationxmax = int(label_info[6]) * x_scale_ratioymax = int(label_info[7]) * y_scale_ration# 将装换后的坐标绘制到resize之后的图片上,检测box是否正确;# cv2.rectangle(img, (int(xmin), int(ymin)), (int(xmax), int(ymax)), (0, 255, 255), 2)# cv2.imshow('Transfer_label', img)# if cv2.waitKey(100) & 0xFF == ord('q'):#     breaknew_info = label_info[0] + ' ' + '0.00' + ' ' + '0' + ' ' + '0.00' + ' ' \+ str('{:.2f}'.format(xmin)) + ' ' + str('{:.2f}'.format(ymin)) + ' ' \+ str('{:.2f}'.format(xmax)) + ' ' + str('{:.2f}'.format(ymax)) \+ ' ' + '0.00' + ' ' + '0.00' + ' ' + '0.00' + ' ' + '0.00' + ' ' + '0.00' \+ ' ' + '0.00' + ' ' + '0.00'w.write(new_info + '\n')w.close()f.close()return 0if __name__ == '__main__':print('----进行照片处理以及标签转换----')# w, h需要%16 == 0resizeImg(544, 800, './images','./resize_images')print('---处理完成---')
同样,解释一下该脚本中的路径:./images代表的是原始图片的存储文件夹;./resize_image代表的是经过处理后的照片存储文件夹;./kitti_labels代表的是上一步骤生成的kitti格式的标签文件存储文件夹;./resize_kitti_labels代表的是经过处理后新生成的kitti格式的标签存储文件夹
生成新的图片比较如下所示(左图为原始照片,右图为resize照片):

生成新的kitti标签如下所示(左边为原图kitti标签,右图为resize之后的kitti标签):

到这里,训练数据集的预处理已经完美结束,接下来则可以开始算法模型的训练了;
这里还需要解释一下,也许你看到这里觉得数据处理的代码有些闲得麻烦了,是的,我也是这么想的,但是,这是一篇博客,为了让更多的人看懂这个过程,本人就将数据处理的每一步都写了一个脚本。实际使用中,本人就使用了一个脚本,等你熟悉整个过程后,也可以自行的修改脚本,不需要这么多步骤分开来。
(四)模型训练配置文件的调整
到这一步就可以开始进行算法模型训练的步骤了;

1. 首先我们先启动容器;

docker run --gpus all -itd -p 8888:8888 nvcr.io/nvidia/tlt-streamanalytics:v2.0_py3 /bin/bash

2. 将训练数据从本机拷贝到容器内;

# 从主机到容器内
docker cp ./datasets kind_kirch:workspace/examples/detectnet_v2/

需要注意:这里的datasets目录下的文件为数据处理最后一步得到的resize数据集,包括图片和标签。如下图:

3. 数据复制好后,进入容器内;

docker exec -it kind_kirch /bin/bash

4. 下载预训练模型;

# 在容器的终端内输入下面的指令,则可以下载预训练模型,在这里,我们使用resnet18作为backbond
ngc registry model download-version "nvidia/tlt_pretrained_detectnet_v2:resnet18"

下载成功后如图所示,下载速度根据网速而定:

5. 修改训练参数配置文件;
模型下载好后我们就可以开始修改配置文件了,在这里我们主要修改两个文件,均在/workspace/example/detectnet_v2/specs目录下:
两个文件分别为:detectnet_v2_tfrecords_kitti_trainval.txtdetectnet_v2_train_resnet18_kitti.txt
detectnet_v2_tfrecords_kitti_trainval.txt主要是将数据转换至tfrecord形式,修改如下:

kitti_config {root_directory_path: "/workspace/examples/detectnet_v2/datasets"image_dir_name: "images"label_dir_name: "labels"image_extension: ".jpg"partition_mode: "random"num_partitions: 2val_split: 14num_shards: 10
}
image_directory_path: "/workspace/tlt-experiments/data/training"

其中需要注意各部分的路径,需要对应到你数据集的存放路径,修改好后,即可进行数据集的转换,在终端中输入如下指令:

# -d 参数后面接修改好后的detectnet_v2_tfrecords_person_trainval.txt文件,-o 参数后面接生成tfrecord数据存储的文件夹
tlt-dataset-convert -d specs/detectnet_v2_tfrecords_person_trainval.txt -o /workspace/examples/detectnet_v2/tfrecord/

执行完指令后,即可得到如下信息,则表明训练数据生成成功:

数据生成后我们则可开始进行训练了
detectnet_v2_train_resnet18_kitti.txt为网络训练具体的配置文件,修改后如下所示:

random_seed: 42
dataset_config {data_sources {tfrecords_path: "/workspace/examples/detectnet_v2/tfrecord/*"image_directory_path: "/workspace/examples/detectnet_v2/datasets/"}image_extension: "jpg"target_class_mapping {key: "person"value: "person"}validation_fold: 0
}
augmentation_config {preprocessing {output_image_width: 544output_image_height: 800min_bbox_width: 1.0min_bbox_height: 1.0output_image_channel: 3}spatial_augmentation {hflip_probability: 0.5zoom_min: 1.0zoom_max: 1.0translate_max_x: 8.0translate_max_y: 8.0}color_augmentation {hue_rotation_max: 25.0saturation_shift_max: 0.20000000298contrast_scale_max: 0.10000000149contrast_center: 0.5}
}
postprocessing_config {target_class_config {key: "person"value {clustering_config {coverage_threshold: 0.00749999983236dbscan_eps: 0.230000004172dbscan_min_samples: 0.0500000007451minimum_bounding_box_height: 20}}}
}
model_config {pretrained_model_file: "/workspace/examples/detectnet_v2/tlt_pretrained_detectnet_v2_vresnet18/resnet18.hdf5"num_layers: 18use_batch_norm: trueobjective_set {bbox {scale: 35.0offset: 0.5}cov {}}training_precision {backend_floatx: FLOAT32}arch: "resnet"
}
evaluation_config {validation_period_during_training: 10first_validation_epoch: 30minimum_detection_ground_truth_overlap {key: "person"value: 0.5}evaluation_box_config {key: "person"value {minimum_height: 20maximum_height: 9999minimum_width: 10maximum_width: 9999}}average_precision_mode: INTEGRATE
}
cost_function_config {target_classes {name: "person"class_weight: 4.0coverage_foreground_weight: 0.0500000007451objectives {name: "cov"initial_weight: 1.0weight_target: 1.0}objectives {name: "bbox"initial_weight: 10.0weight_target: 10.0}}enable_autoweighting: truemax_objective_weight: 0.999899983406min_objective_weight: 9.99999974738e-05
}
training_config {batch_size_per_gpu: 4num_epochs: 30learning_rate {soft_start_annealing_schedule {min_learning_rate: 5e-06max_learning_rate: 5e-04soft_start: 0.10000000149annealing: 0.699999988079}}regularizer {type: L1weight: 3.00000002618e-09}optimizer {adam {epsilon: 9.99999993923e-09beta1: 0.899999976158beta2: 0.999000012875}}cost_scaling {initial_exponent: 20.0
increment: 0.005decrement: 1.0}checkpoint_interval: 10
}
bbox_rasterizer_config {target_class_config {key: "person"value {cov_center_x: 0.5cov_center_y: 1.5cov_radius_x: 1.0cov_radius_y: 1.0bbox_min_radius: 1.0}}deadzone_radius: 0.400000154972

同样,修改配置文件时需要注意配置文件中相关路径的调整【需要注意,这里配置文件的修改,未涉及到超参数的优化,该部分工作大伙们可自行研究,或者留言交流,共同学习,进步】

tfrecords_path: "/workspace/examples/detectnet_v2/tfrecord/*" # tfrecord格式数据集的保存路径
image_directory_path: "/workspace/examples/detectnet_v2/datasets/" # 训练数据集的保存路径
# 预训练模型的路径
pretrained_model_file: "/workspace/examples/detectnet_v2/tlt_pretrained_detectnet_v2_vresnet18/resnet18.hdf5"

7. 开始训练;
待一切准备就绪,我们就可以开始进行算法模型的训练了,在训练之前简单描述几句,这是一篇博客,为了和大家共同交流这个工具的使用流程和经验,关于优化模型训练过程在博客中未做介绍,只是做了简单(wunao)的修改配置文件来实现整个流程,请大家勿喷,谢谢!
开始训练,在终端内输入如下指令:

tlt-train detectnet_v2 -e specs/detectnet_v2_train_resnet18_person.txt -r /workspace/examples/detectnet_v2/backup/train_model/ -k [你自己的key] -n person --gpus 2

在这里本人使用两快GPU进行训练,并且只训练了30个epoch;
指令中各个参数介绍:

tlt-train detectnet_v2
-e <path_to_spec_file>
-r <path_to_experiment_output>
-k [你自己的key]
-n <自定义那model name>
--gpus <GPU数量>

执行上面的训练指令后,则开始正常训练了,如下图:

经过几分钟的训练,30个epoch结束,如下图:

可以看到我们的训练结束了,最后的精度为0.68;【数据少,训练epoch不够】

结尾

本篇博客所做的事到这里已经结束了,希望能对大家有所帮助,互相学习,共同进步!!
本篇博客虽然只训练了一个类别,当然按照这个流程,您也可以训练多类别的,博客主要给您介绍一下这个工具的使用流程,中间的细节部分可能没有讲解清楚,可以留言交流!!
本人的文笔有限 ,要是没有描述清楚的问题,多多谅解,可以留言交流!! 欢迎点赞支持!!
下一篇博客会尽快写好,谢谢!

NVIDIA-TLT训练行人检测模型(一)----算法模型的训练(finetuning)相关推荐

  1. opencv 使用SVM+HOG训练行人检测分类器(INRIA Person Dataset训练集)

    目录 1.训练过程(即代码流程) 2.模型及结果优缺点分析 3.模型建立中发现的问题及改进方法 4.行人检测OpenCv 代码(C++) 1.训练过程(即代码流程) 1. 准备训练样本集合: 包括正样 ...

  2. TF之CNN:基于CIFAR-10数据集训练、检测CNN(2+2)模型(TensorBoard可视化)

    TF之CNN:基于CIFAR-10数据集训练.检测CNN(2+2)模型(TensorBoard可视化) 目录 1.基于CIFAR-10数据集训练CNN(2+2)模型代码 2.检测CNN(2+2)模型 ...

  3. 【算法】决策树模型 集成算法模型GBDT

    一.决策树模型 决策树算法是一种归纳分类算法,它通过对训练集的学习,挖掘有用的规则,对新数据集进行预测.它属于有监督.非参数学习算法,对每个输入使用该分类区域的训练数据得到对应的局部模型.决策树模型的 ...

  4. Deepsort + Yolo 实现行人检测和轨迹追踪

    作者 | 李秋键 出品 | AI科技大本营(ID:rgznai100) 引言 行人检测是近年来计算机视觉领域的研究热点,同时也是目标检测领域中的难点.其目的是识别和定位图像中存在的行人,在许多领域中都 ...

  5. 行人检测资源综述文献

    from: http://blog.csdn.net/GarfieldEr007/article/details/50866089 pedestrian-detection-resource-1-su ...

  6. 【华为云技术分享】【昇腾】ModelArts与Atlas 200 DK云端协同开发——行人检测Demo(提供完整Demo)

    基于ModelArts与Atlas 200 DK 端云协同开发,基于开源数据集,使用ModelArts训练行人检测模型,在本地MindStudio完成模型转换,最终部署到Atlas 200 DK,从数 ...

  7. (转) 行人检测资源 综述文献

    首页 视界智尚 算法技术 每日技术 来打我呀 注册 行人检测资源(上)综述文献 行人检测具有极其广泛的应用:智能辅助驾驶,智能监控,行人分析以及智能机器人等领域.从2005年以来行人检测进入了一个快速 ...

  8. 转:行人检测(Pedestrian Detection)资源

    .论文 CVPR 2012 与行人检测相关的论文 [1] Contextual Boost for Pedestrian Detection  YuanyuanDing, Jing Xiao [2]  ...

  9. 【行人检测】行人检测综述

    1. pipeline Proposal generation:滑窗,粒子窗(particle-window),对象性方法(objectness method),区域建议网络(region propo ...

最新文章

  1. react antD moment
  2. Activity启动流程:Hook实现启动未注册Activity
  3. MVP使用中不好的味道
  4. HDU5982. Relic Discovery
  5. 【JAVA并发编程实战】1、对象的共享
  6. [转]20年来我得到的20条编程经验
  7. office professional 2010 key
  8. 关于x210开发板和主机、虚拟机ping通问题
  9. Java证书工具keytool用法总结
  10. canvas视频录制
  11. Markdown安装/破解/下载
  12. unity-3d摄像头
  13. 猿创征文|【React】组件化入门学习
  14. 计算机右键无法新建excel,右键新建中没有excel怎么删除_右键新建中没有excel怎么办...
  15. 飞思卡尔 I.MX6Q-高分辨率(1080P)视频采集编码
  16. Python_阿里云物联网_数据/图像/音频传输
  17. 观众关注人数超4万,CIOE信息通信展热度持续高涨
  18. Linux的时区修改方法
  19. 哈尔滨工程大学-济海追风-声音信标
  20. CodeForces - 13A Numbers【水题】

热门文章

  1. Hi-Survey Road V2.04更新说明及授权说明
  2. iOS截屏后仿今日头条实现一键分享
  3. AndroidQ 锁屏密码验证流程之GateKeeper解析
  4. 一封来自大佬的密信待查收
  5. emif接口速率问题_各种总线传输速率总结
  6. java如何实现系统定位_如何快速定位到系统中某一功能的具体实现
  7. 打破985校史!她以独作身份投中顶刊,曾因换方向重读博士7年,科研之路也“坎坎坷坷”……...
  8. 倒排表数据结构、通配符查询、拼写纠正详解
  9. 广州商学院 软件工程 博客列表
  10. 注解的作用 常用注解