目标检测和目标分类

图像识别算法是计算机视觉的基础算法,例如VGG,GoogLeNet,ResNet等,这类算法主要是判断图片中目标的种类。

目标检测算法和图像识别算法类似,但是目标检测算法不仅要识别出图像中的物体,还需要获得图像中物体的大小和位置,使用坐标的形式表示出来。如下图:图像识别和目标检测

下面我们举个例子来说明 对于三种物体 人、车、摩托车,对于图像识别,输出列表为三个数字,分别代表图像中物体是人、车、摩托车的概率,例如对于上图的输出值或许是[0.001, 0.998, 0.001]。被预测为车的概率为最高。

而对于目标检测算法来说,它的输出值更像是这样:目标检测算法的输出

其中:pc 为1时代表有物体被检测到,反之,没有物体被检测到,因此其他的输出值可以被忽略

bx 目标的x坐标值 为目标的左上角到物体中心的相对坐标

by 目标的y坐标值 为目标的右下角到物体中心的相对坐标值

bh 区域框bounding box的高

bw 区域框bounding box的宽

c1、c2、c3是目标属于人、车、摩托车的概率,基于此,我们可以使用滑动窗口对整张图片进行处理来定位目标。就像是用一个放大镜去图像的每个区域去查找是否包含目标。这样的方法简单粗暴有效,但是效率极低,计算复杂度太高,所以不会这么去做。

然而,使用滑动窗口时,许多计算都是重复性计算,我们可以使用卷积神经网络的思想。使用卷积计算代替滑动窗口

如上图所示,如果我们用14*14的窗口进行计算,需要4次 5*5的卷积、2*2的max pool、55的卷积、1*1的卷积,而使用16*16的窗口1次计算就可以得到结果,效率更高,这个在Andrew Wu的deeplearning.ai的课程中有详述,至于节省了多少计算量,这里就偷懒不算了,直观印象节省了很大很大的计算量。这里的窗口是固定的,如果需要检测不同大小的物体,需要不同大小的很多的窗口,这也是YOLO算法需要解决的重要问题。

至于目标检测的用处,现在最大的场景就是无人驾驶,在无人驾驶中,需要实时检测出途中的人、车、物体、信号灯、交通标线等,再通过融合技术将各类传感器获得的数据提供给控制中心进行决策。而目标检测相当于无人驾驶系统的眼睛。

在目标检测技术领域,有包含region proposals提取阶段的两阶段(two-stage)检测框架如R-CNN/Fast-RCNN/R-FCN等,再就是端到端的但阶段目标检测框架如YOLO系列和SSD等。

下面我们详述YOLO的思想。

YOLO是You Only Look Once的缩写。这也是为了特别突出YOLO区别于两阶段算法的特点,从名字就可以感受到,YOLO算法速度很快,事实上也是如此。可以看出在同样的设备上,YOLO可以达到45帧每秒的速度。

Grid Cell

在YOLO中,目标图片被划分为Grid Cell,实际应用中,会使用19*19的grid cell, 为了容易理解,这里暂时使用3*3的grid cell。图片被化为3*3的grid cells

图片中的汽车使用红色线框标识出来。

下一步,对于图中的每一个grid cell,我们都会用如下的标签(前文已经解释过具体含义)来标识。

然而,如何将grid cell和物体联系起来呢?

对于右边的汽车,相对简单,由于汽车完全处于右方的grid cell,于是它属于右边的grid cell。

而对于中间的卡车来说,它的bounding box跨越了好几个grid cell。YOLO的做法是,物体的中心处于哪个grid cell,那么物体就属于哪个grid cell, 因此卡车属于最中间的grid cell。

每个grid cell的标签如上图所示,没有物体时,pc为0,其他的值也就没必要关注了,当pc为1时,bx/by为物体中心处于grid cell的相对位置,这时,grid cell的高和宽为1,因此bx/by小于1,bh/bw为物体相对于grid cell的高和宽,其值可以大于1,之后的c1/c2/c3为预测的目标的概率。

这里为了方便起见,只有3类物体,实际应用,会使用80种物体,于是会有c1/c2/c3.../c80。

考虑特殊情况下,如果两个物体的中心处于同一个grid cell的情况。需要使用Anchor Box。

Anchor Box

Anchor Box使得YOLO可以检测出同一个grid cell中包含多个物体的情况。

上图中,人和车的中心都处于中间的grid cell中。Anchor box为标签增加了更多的纬度。如果我们可以对每个物体对应一个anchor box来标识。为了解释方便,这里我们只使用两个anchor box。

每一个grid cell包含两个anchor box,意味着每一个grid cell可以预测两个物体。至于为什么要选择不同形状的anchor box呢?直观印象是这样,我们将物体与anchor box进行比较,看看更像哪个anchor box的形状,和anchor box更像的物体倾向于被识别为anchor box代表的物体形状。例如anchor box1 更像行人的形状,而anchor box2 更像汽车的形状。

如图所示,图像中间位置的grid cell可以用此来标识。这么做的另一个原因是使得模型更专业化。某些输出被用来训练检测像车一样宽形的物体,而另外一个则被用来检测行人一样的高瘦的物体。

那么如何定义、如何判断物体具体相似的形状呢?

Intersection over Union

Intersection over Union IOU 的定义是两个box的交集面积和并集面积的比值。(x1,y1,x2,y2) 分别代表box 左上角和右下角的坐标。

计算IOU的代码如下所示:

def iou(box1, box2):

"""Implement the intersection over union (IoU) between box1 and box2Arguments:box1 -- first box, list object with coordinates (x1, y1, x2, y2)box2 -- second box, list object with coordinates (x1, y1, x2, y2)"""

# Calculate the (y1, x1, y2, x2) coordinates of the intersection of box1 and box2. Calculate its Area.

### START CODE HERE ### (≈ 5 lines)

xi1 = max(box1[0], box2[0])

yi1 = max(box1[1], box2[1])

xi2 = min(box1[2], box2[2])

yi2 = min(box1[3], box2[3])

inter_area = (xi2 - xi1) * (yi2 - yi1)

### END CODE HERE ###

# Calculate the Union area by using Formula: Union(A,B) = A + B - Inter(A,B)

### START CODE HERE ### (≈ 3 lines)

box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])

box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])

union_area = box1_area + box2_area - inter_area

### END CODE HERE ###

# compute the IoU

### START CODE HERE ### (≈ 1 line)

iou = inter_area / union_area

### END CODE HERE ###

return iou

介绍了YOLO中的一些基本概念后,我们先看看YOLO是如何进行目标检测的

假设我们已经训练出了YOLO的模型

首先输入待检测的图片,对图片进行一系列的处理,使得图片的规格符合数据集的要求。

第二,通过模型计算获得预测输出,假如使用的是19*19的grid cell数目,5个anchor box, 80个分类,于是输出的纬度为(1,19,19,5,80+5)

第三,对于输出值进行处理,过滤掉得分低的值,输出值中的Pc 在原论文中被称为confidence 而C被称为 probs,得分为confidence * probs,可以看出,所谓的得分就是含有目标的概率值。

代码实现如下:

def yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold = .6):

"""Filters YOLO boxes by thresholding on object and class confidence.Arguments:box_confidence -- tensor of shape (19, 19, 5, 1)boxes -- tensor of shape (19, 19, 5, 4)box_class_probs -- tensor of shape (19, 19, 5, 80)threshold -- real value, if [ highest class probability score < threshold], then get rid of the corresponding boxReturns:scores -- tensor of shape (None,), containing the class probability score for selected boxesboxes -- tensor of shape (None, 4), containing (b_x, b_y, b_h, b_w) coordinates of selected boxesclasses -- tensor of shape (None,), containing the index of the class detected by the selected boxesNote: "None" is here because you don't know the exact number of selected boxes, as it depends on the threshold.For example, the actual output size of scores would be (10,) if there are 10 boxes."""

# Step 1: Compute box scores

### START CODE HERE ### (≈ 1 line)

box_scores = box_confidence * box_class_probs

### END CODE HERE ###

# Step 2: Find the box_classes thanks to the max box_scores, keep track of the corresponding score

### START CODE HERE ### (≈ 2 lines)

box_classes = K.argmax(box_scores, axis=-1)

# 五个boxes里面分数最高的

box_class_scores = K.max(box_scores, axis=-1)

### END CODE HERE ###

# Step 3: Create a filtering mask based on "box_class_scores" by using "threshold". The mask should have the

# same dimension as box_class_scores, and be True for the boxes you want to keep (with probability >= threshold)

### START CODE HERE ### (≈ 1 line)

filtering_mask = box_class_scores >= threshold

### END CODE HERE ###

# Step 4: Apply the mask to scores, boxes and classes

### START CODE HERE ### (≈ 3 lines)

### filtering_mask 是 False True Flase True组成的列表 通过tf.boolean_mask 过滤掉值为False

### 的值 于是 scores boxes classes 都是至为True的index对应的列表

scores = tf.boolean_mask(box_class_scores, filtering_mask)

boxes = tf.boolean_mask(boxes, filtering_mask)

classes = tf.boolean_mask(box_classes, filtering_mask)

### END CODE HERE ###

return scores, boxes, classes

第四,同一个物体可能会有多个grid cell预测到,那么同一个物体就会有多个bouding box,我们需要留下具有最高pc值的预测值,将其他的预测值过滤掉。如何判断多个bounding box预测的是同一个物体呢,这里就需要使用IOU算法。最后得到的值就是图片中被预测的目标的类型和位置值,再经过一系列计算和转换变成图片上的真实坐标值,使用工具画到原图上。

第四步中的筛选过程被称为Non-max suppression算法

代码如下:

def yolo_non_max_suppression(scores, boxes, classes, max_boxes = 10, iou_threshold = 0.5):

"""Applies Non-max suppression (NMS) to set of boxesArguments:scores -- tensor of shape (None,), output of yolo_filter_boxes()boxes -- tensor of shape (None, 4), output of yolo_filter_boxes() that have been scaled to the image size (see later)classes -- tensor of shape (None,), output of yolo_filter_boxes()max_boxes -- integer, maximum number of predicted boxes you'd likeiou_threshold -- real value, "intersection over union" threshold used for NMS filteringReturns:scores -- tensor of shape (, None), predicted score for each boxboxes -- tensor of shape (4, None), predicted box coordinatesclasses -- tensor of shape (, None), predicted class for each boxNote: The "None" dimension of the output tensors has obviously to be less than max_boxes. Note also that thisfunction will transpose the shapes of scores, boxes, classes. This is made for convenience."""

max_boxes_tensor = K.variable(max_boxes, dtype='int32') # tensor to be used in tf.image.non_max_suppression()

K.get_session().run(tf.variables_initializer([max_boxes_tensor])) # initialize variable max_boxes_tensor

# Use tf.image.non_max_suppression() to get the list of indices corresponding to boxes you keep

### START CODE HERE ### (≈ 1 line) 这是TensorFlow的一个库函数 在很多算法中都会用到

nms_indices = tf.image.non_max_suppression( boxes, scores, max_boxes_tensor, iou_threshold)

### END CODE HERE ###

# Use K.gather() to select only nms_indices from scores, boxes and classes

### START CODE HERE ### (≈ 3 lines)

scores = tf.gather(scores, nms_indices)

boxes = tf.gather(boxes, nms_indices)

classes = tf.gather(classes, nms_indices)

### END CODE HERE ###

return scores, boxes, classes

具体处理的情况如上图,有三个红色的bounding box预测到皮卡,两个黄色的bounding box预测到汽车,我们需要留下pc最高的一个,但是怎么判断哪些bounding box是预测的同一个物体呢,就需要使用IOU方法。

tf.image.non_maxsuppression函数的具体做法是对于所有的boxes先选取具有分数最高pc的box,然后用剩余所有的box和选出的box进行计算IOU的值,当IOU大于iouthreshold时,box被删除掉,然后再在剩余的boxes里取最大值,再做同样的操作,直到boxes的数目为max_boexes_tensor的数目。

处理的结果如下图。

总结一下整个预测过程如下图:

YOLO的检测过程在deepsystems.io中图示的很清晰,可以作为参考。

对于检测的过程下文将对代码部分进行详细分析。

检测的核心代码如下:

def predict(sess, image_file):

"""Runs the graph stored in "sess" to predict boxes for "image_file". Prints and plots the preditions.Arguments:sess -- your tensorflow/Keras session containing the YOLO graphimage_file -- name of an image stored in the "images" folder.Returns:out_scores -- tensor of shape (None, ), scores of the predicted boxesout_boxes -- tensor of shape (None, 4), coordinates of the predicted boxesout_classes -- tensor of shape (None, ), class index of the predicted boxesNote: "None" actually represents the number of predicted boxes, it varies between 0 and max_boxes."""

# Preprocess your image

image, image_data = preprocess_image("images/" + image_file, model_image_size = (608, 608))

# Run the session with the correct tensors and choose the correct placeholders in the feed_dict.

# You'll need to use feed_dict={yolo_model.input: ... , K.learning_phase(): 0})

### START CODE HERE ### (≈ 1 line)

out_scores, out_boxes, out_classes = sess.run([scores, boxes, classes], feed_dict={yolo_model.input: image_data,

input_image_shape: [image.size[1], image.size[0]],

K.learning_phase(): 0})

### END CODE HERE ###

# Print predictions info

print('Found {} boxes for {}'.format(len(out_boxes), image_file))

# Generate colors for drawing bounding boxes.

colors = generate_colors(class_names)

# Draw bounding boxes on the image file

draw_boxes(image, out_scores, out_boxes, out_classes, class_names, colors)

# Save the predicted bounding box on the image

image.save(os.path.join("out", image_file), quality=90)

# Display the results in the notebook

output_image = scipy.misc.imread(os.path.join("out", image_file))

imshow(output_image)

return out_scores, out_boxes, out_classes

代码块中有英文注释,简单来说分为几步:使用preprocess_image函数预处理图片

运行run 获得out_scores, out_boxes, outclasses的处理结果,而scores, boxes, classes是yolo_eval()函数的输出值

使用draw_boxes对讲预测出的框画到图片上

首先先看下preprocess_image函数做了什么事情,函数中每一行都使用中文做了注释

def preprocess_image(img_path, model_image_size):

image_type = imghdr.what(img_path)

## 获得图片类型

image = Image.open(img_path)

## 将图片处理为608*608的固定大小的图片

resized_image = image.resize(tuple(reversed(model_image_size)), Image.BICUBIC)

#读取图片数据存到数组中

image_data = np.array(resized_image, dtype='float32')

# 除去rgb最大值

image_data /= 255.

# 在最前面加一维批处理纬

image_data = np.expand_dims(image_data, 0) # Add batch dimension.

return image, image_data

之后关注yolo_eval函数

def yolo_eval(yolo_outputs,

image_shape,

max_boxes=10,

score_threshold=.6,

iou_threshold=.5):

"""Evaluate YOLO model on given input batch and return filtered boxes.Arguments:yolo_outputs -- 经过模型计算后的值再经过yolo_head函数计算得到的box_confidence, box_xy, box_wh, box_class_probsimage_shape -- 输入图片的维度max_boxes -- 每一张图片中预测出的boxes的最大值score_threshold -- 最小得分值的阈值iou_threshold -- IOU的阈值"""

## 提取box_confidence, box_xy, box_wh, box_class_probs

box_confidence, box_xy, box_wh, box_class_probs = yolo_outputs

## 将boxesd 坐标值转换为四个角的坐标值

boxes = yolo_boxes_to_corners(box_xy, box_wh)

## 过滤掉得分小于score_threshold的boxes 前文之中已有代码实现

boxes, scores, classes = yolo_filter_boxes(

box_confidence, boxes, box_class_probs, threshold=score_threshold)

# Scale boxes back to original image shape.

## 将boxes的坐标值按照image_shape的变换比例还原

height = image_shape[0]

width = image_shape[1]

## 讲image_dims变成(4,)的张量

image_dims = K.stack([height, width, height, width])

image_dims = K.reshape(image_dims, [1, 4])

boxes = boxes * image_dims

# Use one of the functions you've implemented to perform Non-max suppression with a threshold of iou_threshold (≈1 line)

## yolo_non_max_suppression 函数上文中已有解析

scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes, max_boxes = max_boxes, iou_threshold = iou_threshold)

return boxes, scores, classes

于是我们需要关注yolo_outputs是如何获得,即yolo_head函数,至于yolo_boxes_to_corners实现方法比较简,但是需要注意坐标轴的方向

def yolo_boxes_to_corners(box_xy, box_wh):

"""Convert YOLO box predictions to bounding box corners."""

box_mins = box_xy - (box_wh / 2.)

box_maxes = box_xy + (box_wh / 2.)

## 这里返回的值是[y,x,y,x]的组合

return K.concatenate([

box_mins[..., 1:2], # y_min

box_mins[..., 0:1], # x_min

box_maxes[..., 1:2], # y_max

box_maxes[..., 0:1] # x_max

])

yolo_head函数中涉及到的计算较多,数值和维度的变化也比较多,需要深入理解其细节

def yolo_head(feats, anchors, num_classes):

"""Convert final layer features to bounding box parameters.Parameters----------feats : tensor shape为(?,19,19,425)Final convolutional layer features.yolo_model的最后一层输出是一个(m,19,19,5,85)的tensoranchors : array-likeAnchor box widths and heights. 5个anchor的宽和高num_classes : intNumber of target classes. 80个分类Returns-------box_confidence : tensor grid cell内是否含有物体 shape为(?,?,?,5,80)Probability estimate for whether each box contains any object.通过模型最后一层的计算得到的所有的bounding boxes的坐标值(中心坐标和宽高值)box_xy : tensor shape为(?,?,?,5,2)x, y box predictions adjusted by spatial location in conv layer.box_wh : tensor shape为(?,?,?,5,2)w, h box predictions adjusted by anchors and conv spatial resolution.box_class_pred : tensor 物体是80个分类的概率值 shape为(?,?,?,5,1)Probability distribution estimate for each box over class labels."""

num_anchors = len(anchors) ## num_anchors 为5

# Reshape to batch, height, width, num_anchors, box_params.

anchors_tensor = K.reshape(K.variable(anchors), [1, 1, 1, num_anchors, 2])

# 取第2、3维 即19*19

conv_dims = K.shape(feats)[1:3] #conv_dims变成2维 每个维度19个值

# In YOLO the height index is the inner most iteration. [0,1,2,3,4,5,...,18]

conv_height_index = K.arange(0, stop=conv_dims[0])

conv_width_index = K.arange(0, stop=conv_dims[1])

#tile(x, n)函数 将x在各个维度上重复n次,x为张量,n为与x维度数目相同的列表 这里conv_heifh_index只有一维

# 下面这部分看着很绕 但实际上是获得(1,19,19,1,2)维度的位移表

conv_height_index = K.tile(conv_height_index, [conv_dims[1]])

conv_width_index = K.tile(K.expand_dims(conv_width_index, 0), [conv_dims[0], 1])

conv_width_index = K.flatten(K.transpose(conv_width_index))

conv_index = K.transpose(K.stack([conv_height_index, conv_width_index]))

conv_index = K.reshape(conv_index, [1, conv_dims[0], conv_dims[1], 1, 2])

conv_index = K.cast(conv_index, K.dtype(feats)

feats = K.reshape(feats, [-1, conv_dims[0], conv_dims[1], num_anchors, num_classes + 5])

conv_dims = K.cast(K.reshape(conv_dims, [1, 1, 1, 1, 2]), K.dtype(feats))

# Static generation of conv_index: 这种静态方法更容易理解一些

# conv_index = np.array([_ for _ in np.ndindex(conv_width, conv_height)])

# conv_index = conv_index[:, [1, 0]] # swap columns for YOLO ordering.

# conv_index = K.variable(

# conv_index.reshape(1, conv_height, conv_width, 1, 2))

# feats = Reshape(

# (conv_dims[0], conv_dims[1], num_anchors, num_classes + 5))(feats)

box_confidence = K.sigmoid(feats[..., 4:5])

box_xy = K.sigmoid(feats[..., :2])

box_wh = K.exp(feats[..., 2:4])

box_class_probs = K.softmax(feats[..., 5:])

#box_xy 是图片通过model的预测得出的可能是object的85位值中的第1-2位值 代表的是物体被预测的中心位置

#box_wh 是图片通过model的预测得出的object的宽度和高度 再乘以对应的anchors_tensor之后 按照anchors的形状等比例放大

# conv_index 对应的grid cell格子数(从左和从上数的位移)

box_xy = (box_xy + conv_index) / conv_dims

#在这里+conv_index是为了找到对应的cell 将图片的中心落于预测的cell中 得到相对于整张图片的box_xy

box_wh = box_wh * anchors_tensor / conv_dims

return box_confidence, box_xy, box_wh, box_class_probs

特征转换为坐标值的公式如下所示:将特征值转换为(m,19,19,5,85)

对x和y进行sigmoid,使得x,y的值处于0~1之间,并加上对应的offset

对x,y,h,w乘以对应的步幅,即:608/19

使用softmax计算box_class_probs的值

至此,对于YOLO的监测过程已经解析完毕

参考资料

yolo算法的优缺点分析_YOLO算法详细解析(一)相关推荐

  1. yolo算法的优缺点分析_yolo算法介绍

    yolo算法介绍 (2020-06-06 16:49:28) 把Yolo模型搞清楚后不得不再次为人类的智慧感慨,一个巧妙的模型. 要想理解Yolo我们先要搞清楚Yolo到底要解决一个什么问题,解决这个 ...

  2. 算法的优缺点_机器学习算法优缺点 amp; 如何选择

    (点击上方公众号,可快速关注) 转自: 算法与数学之美 主要回顾下几个常用算法的适应场景及其优缺点! 机器学习算法太多了,分类.回归.聚类.推荐.图像识别领域等等,要想找到一个合适算法真的不容易,所以 ...

  3. 算法设计与分析 ——插入排序算法与归并排序算法比较

    插入排序算法与归并排序算法比较 实验目的 通过插入排序算法与归并排序算法效率对比体会算法在求解问题中的重要性. 实验内容 分别编写函数实现插入排序算法和归并排序算法: 利用随机函数产生大量数据存入数组 ...

  4. 算法设计与分析——蒙特卡罗算法(简单、通俗、易懂)C++

    算法设计与分析--蒙特卡罗算法(简单.通俗.易懂) 虽然都是文字描述,但都是较为通俗的语言,相信你看完应该能理解 在实际应用中会遇到一些问题,不论采用确定性算法还是随机性算法,都无法保证每次能到到正确 ...

  5. 图像分析技术大比拼:图像分类、图像识别、目标检测的优缺点分析与算法比较

    计算机视觉是人工智能领域的一个重要分支,它旨在构建能够理解和处理图像.视频等视觉信息的计算机系统.在计算机视觉领域中,图像分类.图像识别和目标检测是三个重要的任务. 一.图像分类 图像分类是计算机视觉 ...

  6. 计算机算法设计与分析读后感,算法设计与分析基础经典读后感有感

    <算法设计与分析基础>是一本由Anany levitin著作,清华大学出版社出版的胶版纸图书,本书定价:49.00元,页数:409,特精心从网络上整理的一些读者的读后感,希望对大家能有帮助 ...

  7. 程振波 算法设计与分析_算法设计与分析

    本书按照教育部*制定的计算机科学与技术专业规范的教学大纲编写,努力与国际计算机学科的教学要求接轨.强调 算法 与 数据结构 之间密不可分的联系,因而强调融数据类型与定义在该类型上的运算于一体的抽象数据 ...

  8. 计算机算法设计与分析教学大纲,算法设计与分析的教与学(教学大纲)

    原标题:算法设计与分析的教与学(教学大纲) 课程代码:**** 课程负责人: **** 课程中文名称:算法设计与分析 课程英文名称:Designand Analysis of Algorithms 课 ...

  9. 【算法设计与分析】算法的时间复杂度(介绍O渐近上界,Ω渐近下界,θ准确的界)

    什么是时间复杂度? 我们先看看一些函数的渐近表达式: 关于时间复杂度的基本要点: 时间复杂度反映的是随着问题规模的变大,计算所需的时间的增长速度,与系数的多少关系不大 算法的渐近时间复杂度,简称时间复 ...

最新文章

  1. log4j每天,每小时产生一日志文件
  2. Android 毛小软件,毛库官方app
  3. 【Tiny4412】最小网络文件系统制作
  4. vmware智能资源调整
  5. AmazonSQS和Spring用于消息传递队列
  6. unity webgl读写txt文件_python Files文件读写操作
  7. 75 jsp基础语法汇总
  8. 常用css属性集(持续更新…)
  9. 3年开发经验,挂在了MyBatis十八连环问上!精通MyBatis源码,有多吃香?
  10. CentOS关机大法之shutdown命令格式
  11. 被拘捕 353 天后,三星“太子”李在镕被判缓刑当庭获释!
  12. 廖雪峰python教程答案
  13. mdui.js手机端侧边滑出导航菜单
  14. 线光谱共焦传感器—专注于3D视觉检测
  15. Framework类库
  16. 机器学习导论——机器学习三要素
  17. ​2020 年大前端技术趋势解读
  18. C#调用百度翻译API
  19. (Mybatis)Example类用法example.createCriteria
  20. Linux命令 - ps命令

热门文章

  1. 【linux】RedHat 7.x 升级 openssh 为 8.x 版本
  2. 多线程导出excel高并发_怎么理解分布式、高并发、多线程
  3. java invocationtarget,启动工程报java.lang.reflect.InvocationTargetException的解决详解
  4. 【BZOJ】1756: Vijos1083 小白逛公园(线段树)
  5. pycharm 如何自动添加头注释,比如时间,作者信息等
  6. 怎样在 Centos 6.8 中安装 Elasticsearch 5.2
  7. windows ffmpeg 推送摄像头数据到rtmp服务
  8. nodejs nodemailer
  9. Android 加入一个动作按钮
  10. 在.NET中调用存储过程