openvino系列 13. 使用 OpenVINO 多模型级联使用:车辆检测与识别示例

此案例演示如何使用 Open Model Zoo 中的两个预训练模型:vehicle-detection-0202 用于对象检测,和 vehicle-attributes-recognition-barrier-0039 用于图像分类。 使用这些模型,我们将从原始图像中检测车辆并识别检测到的车辆的属性(颜色与种类)。

环境描述:

  • 本案例运行环境:Win10,10代i5笔记本
  • IDE:VSCode
  • openvino版本:2022.1
  • 代码链接,9-vehicle-detection-and-recognition

文章目录

  • openvino系列 13. 使用 OpenVINO 多模型级联使用:车辆检测与识别示例
    • 1 关于预训练模型
      • 1.1 vehicle-detection-020X 物体识别预训练模型
      • 1.2 vehicle-attributes-recognition-barrier-00XX 分类模型
    • 2 模块介绍
    • 3 代码
      • 3.1 下载模型
      • 3.2 读取图片
      • 3.3 使用检测模型检测车辆
      • 3.4 使用识别模型检测车辆识别车辆属性
      • 3.5 将检测识别模型串起来

1 关于预训练模型

英特尔的OpenVINO有一个Open Model Zoo,里面包含了非常多的预训练模型。关于我们这个案例,相关的预训练模型包括:

  • [Object detection] vehicle-detection-0200
  • [Object detection] vehicle-detection-0201
  • [Object detection] vehicle-detection-0202
  • [Classification] vehicle-attributes-recognition-barrier-0039
  • [Classification] vehicle-attributes-recognition-barrier-0042

不同的车辆识别模型的区别在于模型复杂度(GFLOPs)不同,当然,越复杂的模型,对应的精度(AP)也就越高。

1.1 vehicle-detection-020X 物体识别预训练模型

vehicle-detection-0200 vehicle-detection-0201 vehicle-detection-0202
High-Level Description This is a vehicle detector that is based on MobileNetV2 backbone with two SSD heads from 1/16 and 1/8 scale feature maps and clustered prior boxes for 256x256 resolution. This is a vehicle detector that is based on MobileNetV2 backbone with two SSD heads from 1/16 and 1/8 scale feature maps and clustered prior boxes for 384x384 resolution. This is a vehicle detector that is based on MobileNetV2 backbone with two SSD heads from 1/16 and 1/8 scale feature maps and clustered prior boxes for 512x512 resolution.
AP @ [ IoU=0.50:0.95 ] 0.254 (internal test set) 0.322 (internal test set) 0.363 (internal test set)
GFlops 0.786 1.768 3.143
MParams 1.817 1.817 1.817
Source framework PyTorch* PyTorch* PyTorch*

三个模型的输入图像尺寸有多不同,输出尺寸一致。

  • 输入:[1,3,256,256]/[1,3,384,384]/[1,3,512,512],对应0200,0201,0202(所以这个就是为什么计算量0202最大的原因)。输入格式:[B,C,H,W],即:[batch size,number of channels,image height,image width]。输入期望BGR格式图片。
  • 输出:[1,1,200,7],即[1,1,N,7],N指的是bounding box的数量。每一个检测框包括七个维度:[image_id, label, conf, x_min, y_min, x_max, y_max]。

对比上面三个模型,我们最终选择vehicle-detection-0202,因为相较于前两个模型,0202的精度更高,而计算量也可以接受。

1.2 vehicle-attributes-recognition-barrier-00XX 分类模型

这里介绍和比较OpenVINO提供的两个分类模型,见下表:

vehicle-attributes-recognition-barrier-0039 vehicle-attributes-recognition-barrier-0042
Car pose Front facing cars Front facing cars
High-level Description This model presents a vehicle attributes classification algorithm r a traffic analysis scenario. This model presents a vehicle attributes classification algorithm for a traffic analysis scenario.
Occlusion coverage <50% <50%
Supported colors White, gray, yellow, red, green, blue, black White, gray, yellow, red, green, blue, black
Supported types Car, van, truck, bus Car, van, truck, bus
GFlops 0.126 0.462
MParams 0.626 11.177
Source framework Caffe* PyTorch*
White Color Accuracy 84.83% 84.20%
gray Color Accuracy 78.01% 77.47%
yellow Color Accuracy 54.01% 61.50%
red Color Accuracy 92.27% 94.65%
green Color Accuracy 83.33% 81.82%
Color average accuracy 81.15 % 82.71%
car 98.26% 97.44%
van 89.16% 86.41%
track 94.27% 96.95%
bus 68.57% 68.57%
Type average accuracy 87.56 % 87.34%

两个模型的输入输出格式尺寸是一样的:

  • 输入:尺寸[1,3,72,72],即[1,C,H,W],代表[number of channels, image height, image width];
  • 输出1:color,车的颜色分类,尺寸[1,7],即车辆七种颜色的概率:[white, gray, yellow, red, green, blue, black];
  • 输出2:type,车的种类分类,尺寸[1,4],即车辆4种种类的概率:[car, van, truck, bus]。

对比上面的两个模型,最终我们选择了vehicle-attributes-recognition-barrier-0039,因为相较于00420039的精度没有低多少,但计算量和参数量却0042小很多。

2 模块介绍

下图对数据流做了大致的解释:

此案例整体的逻辑还会非常直白,容易理解的。我们首先需要导入模型,导入图片,然后对于图片进行一些预处理,使其大小符合第一个车辆检测模型的输入要求。通过车辆检测模型的推断,我们获得检测到车辆的位置信息。接着,我们对输入的图片进行裁剪,使得每张裁剪完的图片只包含检测到的车辆。最后,我们对裁剪完的照片进行预处理,使其大小符合第二个车辆分类模型的输入要求。通过车辆分类模型,我们可以得到这辆车的颜色和种类信息。

当我们运行完所有代码,这里先附上Terminal中打印的信息,从中我们可以直白地看到每个步骤以及其输入输出:

1 - Download detection and recognition models from Open Model Zoo.
2 - Load detection and recognition models from Open Model Zoo.
Get input size - Detection: [512,512]
Get input size - Recognition: [72,72]
3 - Read image, and resize it in order to align with detection model inputs.
- original image shape: (563, 1000, 3)
- original image is reshaped into (1, 3, 512, 512)
4 - Object detection Model Inference. Got bounding box of vehicle detected.
- Box detected: [[0. 0. 0.999808 0.23658293 0.18023151 0.7706103 0.9189388 ]]
5 - Now we crop the image and only left vehicle.
- size of original image: [563,1000]
- size of reshape image and sent into detection model: [512,512]
- Now we refit the scale of bounding box in order to fit the size of original image.
- car position in original image: [[236, 101, 770, 517]]
6 - Classification Model. We got the cropped vehicle image, and resize it in order to align with classification model input.
- Image scale of classification model input: [72,72]
- Model inference. The result contains vehicle colors (white, gray, yellow, red, green, blue, black) and vehicle types (car, bus, truck, van).
- Recognition result: ('Gray', 'Car')
7 - Finally let's combine 2 models and show results.

运行代码后,最终的结果如下:


我们看到,最后那张图识别出来的效果不是很好。但这个案例中,我们不考虑如何改进识别率,而是如何使用预训练模型得到上图效果。

3 代码

3.1 下载模型

我们使用 omz_downloader,它是 openvino-dev 包中的一个命令行工具。 omz_downloader 自动创建目录结构并下载所选模型。 如果模型已下载,则跳过此步骤。 所选模型来自公共目录,这意味着它必须转换为中间表示(IR)。

注意:如果要更改模型,我们可以直接修改模型名称,如"vehicle-detection-0201""vehicle-detection-0202"(关于模型之间的区别,参见Open Model Zoo以及我们上面章节的介绍)。此外,如果要改变精度,需要修改"FP32""FP16""FP16-INT8"中的精度值,不同的型号有不同的模型尺寸和精度值。

相关代码:

import os
import sys
from pathlib import Path
from typing import Tupleimport cv2
import numpy as np
import matplotlib.pyplot as plt
from openvino.runtime import Coreprint("1 - Download detection and recognition models from Open Model Zoo.")
# Directory where model will be downloaded
base_model_dir = "model"
# Model name as named in Open Model Zoo
detection_model_name = "vehicle-detection-0202"
recognition_model_name = "vehicle-attributes-recognition-barrier-0039"
# Selected precision (FP32, FP16, FP16-INT8)
precision = "FP32"# Check if the model exists
detection_model_path = (f"model/intel/{detection_model_name}/{precision}/{detection_model_name}.xml"
)
recognition_model_path = (f"model/intel/{recognition_model_name}/{precision}/{recognition_model_name}.xml"
)# Download the detection model
if not os.path.exists(detection_model_path):download_command = f"omz_downloader " \f"--name {detection_model_name} " \f"--precision {precision} " \f"--output_dir {base_model_dir}"! $download_command
# Download the recognition model
if not os.path.exists(recognition_model_path):download_command = f"omz_downloader " \f"--name {recognition_model_name} " \f"--precision {precision} " \f"--output_dir {base_model_dir}"! $download_commandprint("2 - Load detection and recognition models from Open Model Zoo.")
'''
和常规的OpenVINO流程一样,我们首先初始化推理引擎runtime(Core()),然后读取网络架构和权重
(ie_core.read_model),最后对模型进行编译(ie_core.compile_model)
这里,由于我们检测和识别模型都需要用到这几个步骤,所以定义了一个类:model_init,两个模型的初始化都可以用到。
'''# Initialize inference engine runtime
ie_core = Core()def model_init(model_path: str) -> Tuple:"""Read the network and weights from file, load themodel on the CPU and get input and output names of nodes:param: model: model architecture path *.xml:retuns:input_key: Input node networkoutput_key: Output node networkexec_net: Encoder model networknet: Model network"""# Read the network and corresponding weights from filemodel = ie_core.read_model(model=model_path)# compile the model for the CPU (you can use GPU or MYRIAD as well)compiled_model = ie_core.compile_model(model=model, device_name="CPU")# Get input and output names of nodesinput_keys = compiled_model.input(0)output_keys = compiled_model.output(0)return input_keys, output_keys, compiled_model# de -> detection
# re -> recognition
# Detection model initialization
input_key_de, output_keys_de, compiled_model_de = model_init(detection_model_path)
# Recognition model initialization
input_key_re, output_keys_re, compiled_model_re = model_init(recognition_model_path)# Get input size - Detection
height_de, width_de = list(input_key_de.shape)[2:]
# Get input size - Recognition
height_re, width_re = list(input_key_re.shape)[2:]print("Get input size - Detection: [{0},{1}]".format(height_de, width_de))
print("Get input size - Recognition: [{0},{1}]".format(height_re, width_re))

Terminal的记录:

1 - Download detection and recognition models from Open Model Zoo.
2 - Load detection and recognition models from Open Model Zoo.
Get input size - Detection: [512,512]
Get input size - Recognition: [72,72]

3.2 读取图片

导入图片,然后对于图片进行一些预处理,使其大小符合第一个车辆检测模型的输入要求。

def plt_show(raw_image):"""Use matplot to show image inlineraw_image: input image:param: raw_image:image array"""plt.figure(figsize=(10, 6))plt.axis("off")plt.imshow(raw_image)print('3 - Read image, and resize it in order to align with detection model inputs.')
# Read an image
image_de = cv2.imread("data/car1.jpg")
print("- original image shape: {}".format(image_de.shape))
# Resize to [3, 512, 512]
resized_image_de = cv2.resize(image_de, (width_de, height_de))
# Expand to [1, 3, 512, 512]
input_image_de = np.expand_dims(resized_image_de.transpose(2, 0, 1), 0)
print("- original image is reshaped into {}".format(input_image_de.shape))
# Show image
# plt_show(cv2.cvtColor(image_de, cv2.COLOR_BGR2RGB))

Terminal的记录:

3 - Read image, and resize it in order to align with detection model inputs.
- original image shape: (370, 499, 3)
- original image is reshaped into (1, 3, 512, 512)

3.3 使用检测模型检测车辆

回顾我们使用的识别模型,它的输出:[1,1,200,7],即[1,1,N,7],N指的是bounding box的数量。每一个检测框包括七个维度:[image_id, label, conf, x_min, y_min, x_max, y_max]。其中:

  • image_id - 批次中图像的 ID
  • 标签 - 预测的类别 ID(0 - 车辆)
  • conf - 预测类的置信度
  • (x_min, y_min) - 边界框左上角的坐标
  • (x_max, y_max) - 右下边界框角的坐标

我们通过模型推理得到boxes,即识别的车辆位置以及执行度。然后,我们对其稍作处理,比如删除前两个维度(因为它们只是代表图像ID和预测类别,我们这里只有一个类别),然后,我们删除置信度与检测框为0的box。最后,我们将过滤下来的boxes通过crop_images函数,获得对应原输入照片的车辆位置(需要注意,我们一开始获得的车辆位置坐标是基于缩放过的图片基础上的)。

相关代码:

def crop_images(bgr_image, resized_image, boxes, threshold=0.6) -> np.ndarray:"""Use bounding boxes from detection model to find the absolute car position:param: bgr_image: raw image:param: resized_image: resized image:param: boxes: detection model returns rectangle position:param: threshold: confidence threshold:returns: car_position: car's absolute position"""# Fetch image shapes to calculate ratio(real_y, real_x), (resized_y, resized_x) = bgr_image.shape[:2], resized_image.shape[:2]ratio_x, ratio_y = real_x / resized_x, real_y / resized_yprint("- size of original image: [{},{}]".format(real_y, real_x))print("- size of reshape image and sent into detection model: [{},{}]".format(resized_y, resized_x))print("- Now we refit the scale of bounding box in order to fit the size of original image.")# Find the boxes ratioboxes = boxes[:, 2:]# Store the vehicle's positioncar_position = []# Iterate through non-zero boxesfor box in boxes:# Pick confidence factor from last place in arrayconf = box[0]if conf > threshold:# Convert float to int and multiply corner position of each box by x and y ratio# In case that bounding box is found at the top of the image, # we position upper box bar little bit lower to make it visible on image (x_min, y_min, x_max, y_max) = [int(max(corner_position * ratio_y * resized_y, 10)) if idx % 2 else int(corner_position * ratio_x * resized_x)for idx, corner_position in enumerate(box[1:])]car_position.append([x_min, y_min, x_max, y_max])return car_positionprint("4 - Object detection Model Inference. Got bounding box of vehicle detected.")
# Run Inference
boxes = compiled_model_de([input_image_de])[output_keys_de]
# 删除输出的第0,第1维度。
boxes = np.squeeze(boxes, (0, 1))
# 删除那些置信度以及bounding box坐标只有0的bounding box
boxesFilter = []
for idx,box in enumerate(boxes):if np.all(box[2:]==0):passelse:boxesFilter.append(box)
boxesFilter = np.array(boxesFilter)
print("- Box detected: {}".format(boxesFilter))
print("5 - Now we crop the image and only left vehicle.")
# Find car position
car_position = crop_images(image_de, resized_image_de, boxes)
print("- car position in original image: {}".format(car_position))

Terminal中的打印:

4 - Object detection Model Inference. Got bounding box of vehicle detected.
- Box detected: [[0. 0. 0.99987304 0.57274306 0.4301208  0.7870749 0.6561528 ][0. 0. 0.99982446 0.5723677 0.15962084 0.70758444 0.28779876][0. 0. 0.8183867  0.8989585  0.40307313 0.9999551 0.6037573 ][0. 0. 0.04074085 0.91444695 0.01791241 0.95828915 0.08315378]]
5 - Now we crop the image and only left vehicle.
- size of original image: [370,499]
- size of reshape image and sent into detection model: [512,512]
- Now we refit the scale of bounding box in order to fit the size of original image.
- car position in original image: [[285, 159, 392, 242], [285, 59, 353, 106], [448, 149, 498, 223]]

3.4 使用识别模型检测车辆识别车辆属性

选择一个检测到的框,然后裁剪到包含车辆的区域以使用识别模型进行测试。同样,我们需要调整输入图像的大小并运行推理。

识别结果包含车辆颜色(白色、灰色、黄色、红色、绿色、蓝色、黑色)和车辆类型(汽车、公共汽车、卡车、货车)。 接下来,我们需要计算每个属性的概率。 最后,我们确定最大概率作为结果。

相关代码:

print("6 - Classification Model. We got the cropped vehicle image, and resize it in order to align with classification model input.")
# Select a vehicle to recognize
pos = car_position[0]
# Crop the image with [y_min:y_max, x_min:x_max]
test_car = image_de[pos[1]:pos[3], pos[0]:pos[2]]
# resize image to input_size
resized_image_re = cv2.resize(test_car, (width_re, height_re))
print("- Image scale of classification model input: [{},{}]".format(width_re,height_re))
input_image_re = np.expand_dims(resized_image_re.transpose(2, 0, 1), 0)
#plt_show(cv2.cvtColor(test_car, cv2.COLOR_BGR2RGB))def vehicle_recognition(compiled_model_re, input_size, raw_image):"""Vehicle attributes recognition, input a single vehicle, return attributes:param: compiled_model_re: recognition net :param: input_size: recognition input size:param: raw_image: single vehicle image:returns: attr_color: predicted colorattr_type: predicted type"""# vehicle's attributecolors = ['White', 'Gray', 'Yellow', 'Red', 'Green', 'Blue', 'Black']types = ['Car', 'Bus', 'Truck', 'Van']# resize image to input sizeresized_image_re = cv2.resize(raw_image, input_size)input_image_re = np.expand_dims(resized_image_re.transpose(2, 0, 1), 0)# Run Inference# Predict Resultpredict_colors = compiled_model_re([input_image_re])[compiled_model_re.output(1)]# delete the dim of 2, 3predict_colors = np.squeeze(predict_colors, (2, 3))predict_types = compiled_model_re([input_image_re])[compiled_model_re.output(0)]predict_types = np.squeeze(predict_types, (2, 3))attr_color, attr_type = (colors[np.argmax(predict_colors)],types[np.argmax(predict_types)])return attr_color, attr_typeprint("- Model inference. The result contains vehicle colors (white, gray, yellow, red, green, blue, black) and vehicle types (car, bus, truck, van).")
print(f"- Recognition result: {vehicle_recognition(compiled_model_re, (72, 72), test_car)}")

Terminal中打印:

6 - Classification Model. We got the cropped vehicle image, and resize it in order to align with classification model input.
- Image scale of classification model input: [72,72]
- Model inference. The result contains vehicle colors (white, gray, yellow, red, green, blue, black) and vehicle types (car, bus, truck, van).
- Recognition result: ('White', 'Car')

注意,上面的代码,我们只是选中了检测框中的一个,进行图像识别。

3.5 将检测识别模型串起来

最后,我们将两个模型串起来,最后返回类似结果章节的图。代码如下:

print("7 - Finally let's combine 2 models and show results.")def convert_result_to_image(compiled_model_re, bgr_image, resized_image, boxes, threshold=0.6):"""Use Detection model boxes to draw rectangles and plot the result:param: compiled_model_re: recognition net:param: input_key_re: recognition input key:param: bgr_image: raw image:param: resized_image: resized image:param: boxes: detection model returns rectangle position:param: threshold: confidence threshold:returns: rgb_image: processed image"""# Define colors for boxes and descriptionscolors = {"red": (255, 0, 0), "green": (0, 255, 0)}# Convert base image from bgr to rgb formatrgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)# Find cars' positionscar_position = crop_images(image_de, resized_image, boxes)idx = 0for x_min, y_min, x_max, y_max in car_position:print("- Final car position {}: [{}]".format(idx, car_position[idx]))# Run vehicle recognition inferenceattr_color, attr_type = vehicle_recognition(compiled_model_re, (72, 72), image_de[y_min:y_max, x_min:x_max])print("- Final car recognition result: {}, {}".format(attr_color, attr_type))# close the vehicle windowplt.close()# Draw bounding box based on position# Parameters in rectangle function are: image, start_point, end_point, color, thicknessrgb_image = cv2.rectangle(rgb_image, (x_min, y_min), (x_max, y_max), colors["red"], 2)# Print vehicle attributes # parameters in putText function are: img, text, org, fontFace, fontScale, color, thickness, lineTypergb_image = cv2.putText(rgb_image, f"{attr_color} {attr_type}",(x_min, y_min - 10),cv2.FONT_HERSHEY_SIMPLEX,1,colors["green"],5,cv2.LINE_AA)idx += 1return rgb_imageplt_show(convert_result_to_image(compiled_model_re, image_de, resized_image_de, boxes))

Terminal打印:

- size of original image: [370,499]
- size of reshape image and sent into detection model: [512,512]
- Now we refit the scale of bounding box in order to fit the size of original image.
- Final car position 0: [[285, 159, 392, 242]]
- Final car recognition result: White, Car
- Final car position 1: [[285, 59, 353, 106]]
- Final car recognition result: Red, Car
- Final car position 2: [[448, 149, 498, 223]]
- Final car recognition result: White, Truck

openvino系列 13. 使用 OpenVINO 多模型级联使用:车辆检测与识别示例相关推荐

  1. OpenVINO系列03_安装OpenVINO

    一.Windows上安装OpenVINO 1.图文演示 https://blog.csdn.net/kan2016/article/details/89519607 2.视频演示 https://ww ...

  2. openvino系列 12. Model Optimizer:PaddlePaddle 模型转化 IR 模型

    openvino系列 12. Model Optimizer:PaddlePaddle 模型转化 IR 模型 本案例展示了如何将 MobileNetV3 模型从 PaddleHub加载到本地,最终转换 ...

  3. openvino系列 15. OpenVINO OCR

    openvino系列 15. OpenVINO OCR 此案例主要解释如何使用 OpenVINO OCR 模型进行字体检测(detection)和识别(recognition).总体上尝试下来的,Op ...

  4. 使用OpenVINO运行PPTracking下FairMOT多目标跟踪模型

    图1  MOT 行人检测[1] 多对象追踪(Multi- Object Tracking, MOT) 在计算机视觉领域有着广泛且重要的应用.大到可以用在多目标导弹跟踪.市中心人流统计, 小到可以用在统 ...

  5. openvino c++推理华盛顿大学BackgroundMattingV2模型人像抠图

    使用openvino c++推理华盛顿大学BackgroundMattingV2模型人像抠图. 基本性能:仅使用CPU可以实现720P实时推理 开源项目:https://github.com/king ...

  6. Yolov5 系列1--- Yolo发展史以及Yolov5模型详解

    最近在做检测相关的工作,几年前分析过faster-rcnn的代码和论文.现在,把yolov5这个最新且快的模型进行梳理.本系列会从Yolo的发展历程开始,到损失函数,mAP的概念,最后到如何在代码层面 ...

  7. 机器学习系列笔记十三: 集成学习/模型聚合

    机器学习系列笔记十三: 集成学习/模型聚合 文章目录 机器学习系列笔记十三: 集成学习/模型聚合 什么是集成学习 Voting Hard Voting 模拟实现Hard Voting 集成学习 使用V ...

  8. Threejs系列--13游戏开发--沙漠赛车游戏【使用效果合成器添加高级效果】

    Threejs系列--13游戏开发--沙漠赛车游戏[使用效果合成器添加高级效果] 序言 目录结构 代码一览 Blur.js代码 Glows.js代码 Application.js代码 代码解读 运行结 ...

  9. SAP PM 初级系列13 - PM Revision

    SAP PM 初级系列13 - PM Revision SAP PM模块的Revision用于将维修通知或者维修工单分组,在维修的时候执行相关的工作. PM Revision一般在工厂shutdown ...

  10. 「微服务系列 13」熔断限流隔离降级

    我们知道微服务分布式依赖关系错综复杂,比方说前端的请求转化为后端调用的服务请求,一个前端请求会转为成很多个后端调用的服务请求,那么这个时候后台的服务出现不稳定或者延迟,如果没有好的限流熔断措施,可能会 ...

最新文章

  1. JavaScript专题之模拟实现call和apply
  2. 三维人脸识别研究进展综述(附pdf)
  3. 面向对象数据库NDatabase_初识
  4. pythonvim编辑教程_Pycharm学习教程(6) Pycharm作为Vim编辑器使用
  5. 了解 SharePoint 2010 开发中的关键设计决定
  6. LeetCode 2018. 判断单词是否能放入填字游戏内(模拟)
  7. java中doloop语句_Java中的do-while循环——通过示例学习Java编程(11)
  8. [LeetCode] NO. 8 String to Integer (atoi)
  9. python如何爬取sci论文_通过爬虫确定SCI期刊的发表周期
  10. [Windows核心编程]堆
  11. 到外企应聘如何准备英文简历(Tips for Resume)
  12. Typecho单栏式Major主题模板V1.8
  13. 开源硬件USB抓包及协议分析工具分享
  14. 2021年最新程序员培训机构排名,学习前避坑必看
  15. 硬件设计——PWM原理与设计
  16. MySQL备份与恢复
  17. python 手机App数据抓取实战一
  18. C语言四川麻将算法,四川麻将胡牌算法
  19. 盗贼之海服务器维护时间,盗贼之海常见问题解决方法
  20. edge microsoff 连不上网_win10电脑连不上网的三种解决方法

热门文章

  1. PMP杂谈--配置管理系统和变更控制系统
  2. 51单片机实现超声波测距
  3. MASM入门(二)MASM基本语法
  4. 觅风易语言[11-20]
  5. 计算机硬件维修的步骤和方法,计算机硬件组装与维护教程
  6. python项目报告怎么写_python项目
  7. 网络操作系统之网络操作系统的功能
  8. 谷歌金山词霸正式发布--免费午餐越来越多
  9. 电脑常识——更改鼠标光标(另附一套MC指针)
  10. 逆向破解crackme简要分析