YOLO的Python实现以及 OpenCV
Darknet 实现 YOLO
从头开始开发 YOLO模型不容易,所以我们要使用预训练模型在项目里进行目 标检测。你可以在 https://pjreddie.com里到所有可用的预训练模型。这是 Joseph C. Redmon的主页,他是 Darknet的维护者。
注意 Darknet是C和CUDA开发的开源神经网络框架,它很快,很容易安装,支持 CpU 和 GpU计算。
在子页 (https://pjreddie.com/darknet/yolo/), 你可以找到 YOLO算法的所有信息。你可以从这个网页下载多个预训练模型的权重。对于每一个模型,你需要二个文件 :
- 一个 .cfg文件,它包含网络结构.
- 一个 .weights文件,它包含训练后的权重
为了给你这些文件的内容, .cfg文件包含使用的层的信息等,示例如:
[convolutional] batch_normalize=1 filters=64
size=3
stride=1 pad=1
activation=leaky
这告 诉你有特殊的卷积层。最重要的信息是:
- Network architecture
- Anchor boxes
- Number of classes
- Learning rate and other parameters used
- Batch size
其它文件(.weights)包含预训练权重以进行推断。注意它们不能保存为Keras兼容的格式 (如 .h5文件),所以它们不能加载到 Keras模型,除非你先转换。
有一些非标准的工具可以转换这些文件,因为格式不是固定的 ( YOLOv2 和 YOLOv3有些不同)。如果你感兴趣以 YOLO v2,你可以使用 YAD2K 库 (另一个是 Darknet 2 Keras),可以在 https://github.com/allanzelener/YAD2K里找到。
注意这不能处理 YOLOv3 .cfg文件。相信我,我试过。但是如果你满意 YOLOv2, 你可以用这个目录的代码转换 .weight文件到Keras友好的格式。有个目录实现了 YOLOv3 的转换,在https://github.com/qqwweee/keras-yolo3。它有些局限,但是它是转换的很好的起点。 但是有便好的方法,使用预训练模型,那就是使用 OpenCV, 后面我们会看到。
用 Darknet检测对象
如果你只是想对图像进行分类,最容易的办法是按 darknet网站的指示。我们看一下如何做。如果你使用Linux或 MacOS X系统,这些指令有用。 在Windows里,你要安装 gcc和别的工具。如网站所述,安装只需要几行代码:
git clone https://github.com/pjreddie/darknet cd darknet
make
wget https://pjreddie.com/media/files/yolov3.weights
这里你就可以进行目标检测了:
./darknet detect cfg/yolov3.cfg yolov3.weights table.jpg
注意,权重文件很大,下载时要注意这一点,使用CPU这很慢,MacBook Pro 2018需要18秒来下载。见图 7-2。
图7-2. YOLOv3使用 darknet进行图像检测
黙认使用0.25的阈值。但是你可以使用不同的参数。你必须改变XYZ到你想要的值。
黙认使用0.25的阈值。但是你可以使用不同的参数。你必须改变XYZ到你想要的值。.
这个方法于于目标检测 很好,但是很难在python项目里使用。要这样做,你需要在你的代码里使用预训练的模型。有几个方法,最容易的是使用opencv库。如果你处理图像,你很可能已经使用过这个库。如果你没有听说过,建议你试一下,因为它是处理图像很有名的库。你可以看官网 https:// opencv.org.
你可以在网上下载本章的完整代码,但是我们只简单的讨论重要的部分。
你需要安装最新的opencv库。 用下面的代码检测版本:
import cv2
print (cv2. version )
我们需要从https:// pjreddie.com 下载三个文件:
- coco.names
- yolov3.cfg
- yolov3.weights
coco.names 包含了预训练模型能分类的标签。yolov3.cfg 和yolov3.weights是模型配置参数 (前面已讨论过)且我们需要使用 权重。为了你方便,yolov3.weights大约 240MB容量,你可以下载 ZIP 文件自 http://toe.lt/r。在代码里我们需要指明文件在哪里。例如,你使用下面的代码:
weightsPath = "yolo-coco/yolov3.weights" configPath = "yolo-coco/yolov3.cfg"
你要改变文件的位置。 OpenCV提供了函数来加载权重而不需要转换它们:
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)
这很舒服,因为你不需要分析和写加载函数。它返回模型对象供你后面推断。如果你记得本章开始的讨论,你要得到输出层来得到所有需要的信息,像边框和预测分类。我们用下面的代码很容易做到:
ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
getUnconnectedOutLayers()函数返回带用unconnected输出的层, 正是我们想要的。ln这量含有下面的层:
['yolo_82', 'yolo_94', 'yolo_106']
然后我们要改变图像大小到 416x416并归一化它:
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)
然后用它作为模型的输入:
net.setInput(blob)
然后我们用 forward()函数向前传递给训练模型:
layerOutputs = net.forward(ln)
我们还没有完成,不要休息。我们要提取边框,我们保存在列表里,然后是置信,然后是预测分类.
我们初始化列表:
boxes = [] confidences = [] classIDs = []
for output in layerOutputs: for detection in output:
现在分 数保存在第5个检测变量的元素里,我们提取分类用np. argmax(scores):
scores = detection[5:] classID = np.argmax(scores)
置信度是预测分类的分值:
confidence = scores[classID]
我们想要预测置信度大于0的。我们选择限度为0.15。预测边框包含于检测变量的前4个里:
box = detection[0:4] * np.array([W, H, W, H]) (centerX, centerY, width, height) = box.astype("int")
如果你记得,YOLO预测边框的中心点,所在我们要的提取左上角位置:
x = int(centerX - (width / 2)) y = int(centerY - (height / 2))
然后我们将发现的值添加到列表
boxes.append([x, y, int(width), int(height)]) confidences.append(float(confidence)) classIDs.append(classID)
然后我们使用 non-maxima 抑制 (上一节讨论过)。 OpenCV 也提供了函数:
idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.6,0.2)
函数需要下面的参数:
- 边框集合 (保存于 boxes变量)
- 置信度集合 (保存于confidences变量)
- 按分值过滤边框的阈值 (前面代码是0.6)
- non-maximum抑制的阈值 (前面的代码是0.2)
然我们得到准确的位置:
for i in idxs.flatten():
# extract the bounding box coordinates (x, y) = (boxes[i][0], boxes[i][1])
(w, h) = (boxes[i][2], boxes[i][3])
你可以在图 7-3看到结果。
图 7-3. YOLOv3用 OpenCV获得的结果
这正是它应有的—与图7-2一样的结果。另外,我们有预测边框的概率。你可以看到这是多么的容易。你只需要添加几行代码到你的项目。
这正是它应有的—与图7-2一样的结果。另外,我们有预测边框的概率。你可以看到这是多么的容易。你只需要添加几行代码到你的项目。
记住我们使用预训练模型只能检测数据集里的对象。如果你要用不同的对象,你要微调模型,或重新训练模型。从头训练模型超出了本书的范围。但是下一节我会给你一些提示。