第十六周周报:单发的目标检测系列

[复制链接]
发表于 2026-1-10 12:36:57 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目次
择要
Abstract
一、SSD
1.1 模子结构
1.2 代码
二、YOLO
三、Termius
总结


择要

本周紧张学习单阶段的目标检测算法,如SSD、YOLO模子。具体学习了每个模子的原理,以及SSD和YOLO模子之间的异同。在本篇博客中将展示SSD的PyTorch实当代码,以及在第三章会介绍一下Termius的使用,便于连接服务器。

Abstract

This week, I will mainly learn single-stage object detection algorithms such as SSD and YOLO models. I have studied in detail the principles of each model, as well as the similarities and differences between SSD and YOLO models. In this blog post, we will showcase the PyTorch implementation code for SSD, and in Chapter 3, we will introduce the use of Termius for easy server connection.

一、SSD

1.1 模子结构

上一篇博客学习了R-CNN系列,其都为2步走系列,先进行候选地域的提取,再进行候选地域的分类等使用。而Single Shot MultiBox Detector(SSD)为单发的目标检测,即通过一次网络便可以完成边框的猜测和类概率的输出。
   正由于是Single Shot,极大进步了目标检测的速率,如下图所示:
  

  SSD能做到每秒59帧的速率,比上一篇博客中提到的Faster R-CNN的每秒7帧,拥有更高的准确率。
  我们来看看SSD是做了哪些改进得到云云优秀的效果吧!SSD结构图如下所示:

SSD接入 300x300 巨细的图像,起首,通过ImageNet上预训练的分类模子VGG-16(去除末了1000维的全连接层)进行特性提取。
SSD的焦点头脑是,通过差别维度的特性图像,到达猜测差别巨细目标的目标。如上图所示,在VGG-16提取特性之后,通过卷积将特性图像变革为差别巨细和维度的特性图像,如:FC6——FC11。然后,在差别维度的特性图像(检测层)上进行目标检测。
关键在于怎样对不消检测层进行检测。这里作者对每一层设置了差别巨细的anchor,同一层的anchor也有差别的比例,如1:1、1:2、1:3、2:1、3:1。在上述6个差别的检测层中,以每个特性图像的像素点为中心,同一层中每个像素点绘制巨细相同,但有5种比例的anchor。如下图所示:

正由于特性图像的巨细差别,以是纵然巨细相同的anchor得到的特性,再映射回原图像,也会得到巨细差别的原图像截取。于是,SSD通过这种方式就能实现对差别尺寸的目标进行检测。
在每个检测层中,anchor像滑动窗口一样依次滑动,然后再通过 3x3 的卷积连接两个并行层,即分类和框回归。 
颠末上述步骤,再将猜测框和分类概率映射回原图像。通过非极大值避免等使用留下终极的猜测框,与样本框进行偏差计算,再经反向传播更新参数得以训练SSD模子。
   正由于上述所说anchor是作者事先设置好的,针对于每一层都有差别巨细的anchor,以是不必要后期训练,也不必要通过算法进行候选地域的提取,极大进步了目标检测的速率。
  1.2 代码

骨干网络采用在ImageNet上预训练的VGG-16来提取图像特性,下面是SSD网络模子PyTorch代码:
  1. import colorsys
  2. import os
  3. import time
  4. import warnings
  5. import numpy as np
  6. import torch
  7. import torch.backends.cudnn as cudnn
  8. from PIL import Image, ImageDraw, ImageFont
  9. from nets.ssd import SSD300
  10. from utils.anchors import get_anchors
  11. from utils.utils import (cvtColor, get_classes, preprocess_input, resize_image,
  12.                          show_config)
  13. from utils.utils_bbox import BBoxUtility
  14. warnings.filterwarnings("ignore")
  15. #--------------------------------------------#
  16. #   使用自己训练好的模型预测需要修改3个参数
  17. #   model_path、backbone和classes_path都需要修改!
  18. #   如果出现shape不匹配
  19. #   一定要注意训练时的config里面的num_classes、
  20. #   model_path和classes_path参数的修改
  21. #--------------------------------------------#
  22. class SSD(object):
  23.     _defaults = {
  24.         #--------------------------------------------------------------------------#
  25.         #   使用自己训练好的模型进行预测一定要修改model_path和classes_path!
  26.         #   model_path指向logs文件夹下的权值文件,classes_path指向model_data下的txt
  27.         #
  28.         #   训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。
  29.         #   验证集损失较低不代表mAP较高,仅代表该权值在验证集上泛化性能较好。
  30.         #   如果出现shape不匹配,同时要注意训练时的model_path和classes_path参数的修改
  31.         #--------------------------------------------------------------------------#
  32.         "model_path"        : 'logs/ssd_weights.pth',
  33.         "classes_path"      : 'model_data/voc_classes.txt',
  34.         #---------------------------------------------------------------------#
  35.         #   用于预测的图像大小,和train时使用同一个即可
  36.         #---------------------------------------------------------------------#
  37.         "input_shape"       : [300, 300],
  38.         #-------------------------------#
  39.         #   主干网络的选择
  40.         #   vgg或者mobilenetv2或者resnet50
  41.         #-------------------------------#
  42.         "backbone"          : "vgg",
  43.         #---------------------------------------------------------------------#
  44.         #   只有得分大于置信度的预测框会被保留下来
  45.         #---------------------------------------------------------------------#
  46.         "confidence"        : 0.5,
  47.         #---------------------------------------------------------------------#
  48.         #   非极大抑制所用到的nms_iou大小
  49.         #---------------------------------------------------------------------#
  50.         "nms_iou"           : 0.45,
  51.         #---------------------------------------------------------------------#
  52.         #   用于指定先验框的大小
  53.         #---------------------------------------------------------------------#
  54.         'anchors_size'      : [30, 60, 111, 162, 213, 264, 315],
  55.         #---------------------------------------------------------------------#
  56.         #   该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize,
  57.         #   在多次测试后,发现关闭letterbox_image直接resize的效果更好
  58.         #---------------------------------------------------------------------#
  59.         "letterbox_image"   : False,
  60.         #-------------------------------#
  61.         #   是否使用Cuda
  62.         #   没有GPU可以设置成False
  63.         #-------------------------------#
  64.         "cuda"              : False,
  65.     }
  66.     @classmethod
  67.     def get_defaults(cls, n):
  68.         if n in cls._defaults:
  69.             return cls._defaults[n]
  70.         else:
  71.             return "Unrecognized attribute name '" + n + "'"
  72.     #---------------------------------------------------#
  73.     #   初始化ssd
  74.     #---------------------------------------------------#
  75.     def __init__(self, **kwargs):
  76.         self.__dict__.update(self._defaults)
  77.         for name, value in kwargs.items():
  78.             setattr(self, name, value)
  79.         #---------------------------------------------------#
  80.         #   计算总的类的数量
  81.         #---------------------------------------------------#
  82.         self.class_names, self.num_classes  = get_classes(self.classes_path)
  83.         self.anchors                        = torch.from_numpy(get_anchors(self.input_shape, self.anchors_size, self.backbone)).type(torch.FloatTensor)
  84.         if self.cuda:
  85.             self.anchors = self.anchors.cuda()
  86.         self.num_classes                    = self.num_classes + 1
  87.         
  88.         #---------------------------------------------------#
  89.         #   画框设置不同的颜色
  90.         #---------------------------------------------------#
  91.         hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)]
  92.         self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
  93.         self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors))
  94.         self.bbox_util = BBoxUtility(self.num_classes)
  95.         self.generate()
  96.         
  97.         show_config(**self._defaults)
  98.     #---------------------------------------------------#
  99.     #   载入模型
  100.     #---------------------------------------------------#
  101.     def generate(self, onnx=False):
  102.         #-------------------------------#
  103.         #   载入模型与权值
  104.         #-------------------------------#
  105.         self.net    = SSD300(self.num_classes, self.backbone)
  106.         device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  107.         self.net.load_state_dict(torch.load(self.model_path, map_location=device))
  108.         self.net    = self.net.eval()
  109.         print('{} model, anchors, and classes loaded.'.format(self.model_path))
  110.         if not onnx:
  111.             if self.cuda:
  112.                 self.net = torch.nn.DataParallel(self.net)
  113.                 self.net = self.net.cuda()
  114.     #---------------------------------------------------#
  115.     #   检测图片
  116.     #---------------------------------------------------#
  117.     def detect_image(self, image, crop = False, count = False):
  118.         #---------------------------------------------------#
  119.         #   计算输入图片的高和宽
  120.         #---------------------------------------------------#
  121.         image_shape = np.array(np.shape(image)[0:2])
  122.         #---------------------------------------------------------#
  123.         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。
  124.         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB
  125.         #---------------------------------------------------------#
  126.         image       = cvtColor(image)
  127.         #---------------------------------------------------------#
  128.         #   给图像增加灰条,实现不失真的resize
  129.         #   也可以直接resize进行识别
  130.         #---------------------------------------------------------#
  131.         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image)
  132.         #---------------------------------------------------------#
  133.         #   添加上batch_size维度,图片预处理,归一化。
  134.         #---------------------------------------------------------#
  135.         image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0)
  136.         with torch.no_grad():
  137.             #---------------------------------------------------#
  138.             #   转化成torch的形式
  139.             #---------------------------------------------------#
  140.             images = torch.from_numpy(image_data).type(torch.FloatTensor)
  141.             if self.cuda:
  142.                 images = images.cuda()
  143.             #---------------------------------------------------------#
  144.             #   将图像输入网络当中进行预测!
  145.             #---------------------------------------------------------#
  146.             outputs     = self.net(images)
  147.             #-----------------------------------------------------------#
  148.             #   将预测结果进行解码
  149.             #-----------------------------------------------------------#
  150.             results     = self.bbox_util.decode_box(outputs, self.anchors, image_shape, self.input_shape, self.letterbox_image,
  151.                                                     nms_iou = self.nms_iou, confidence = self.confidence)
  152.             #--------------------------------------#
  153.             #   如果没有检测到物体,则返回原图
  154.             #--------------------------------------#
  155.             if len(results[0]) <= 0:
  156.                 return image
  157.             top_label   = np.array(results[0][:, 4], dtype = 'int32')
  158.             top_conf    = results[0][:, 5]
  159.             top_boxes   = results[0][:, :4]
  160.         #---------------------------------------------------------#
  161.         #   设置字体与边框厚度
  162.         #---------------------------------------------------------#
  163.         font = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * np.shape(image)[1] + 0.5).astype('int32'))
  164.         thickness = max((np.shape(image)[0] + np.shape(image)[1]) // self.input_shape[0], 1)
  165.         #---------------------------------------------------------#
  166.         #   计数
  167.         #---------------------------------------------------------#
  168.         if count:
  169.             print("top_label:", top_label)
  170.             classes_nums    = np.zeros([self.num_classes])
  171.             for i in range(self.num_classes):
  172.                 num = np.sum(top_label == i)
  173.                 if num > 0:
  174.                     print(self.class_names[i], " : ", num)
  175.                 classes_nums[i] = num
  176.             print("classes_nums:", classes_nums)
  177.         #---------------------------------------------------------#
  178.         #   是否进行目标的裁剪
  179.         #---------------------------------------------------------#
  180.         if crop:
  181.             for i, c in list(enumerate(top_boxes)):
  182.                 top, left, bottom, right = top_boxes[i]
  183.                 top     = max(0, np.floor(top).astype('int32'))
  184.                 left    = max(0, np.floor(left).astype('int32'))
  185.                 bottom  = min(image.size[1], np.floor(bottom).astype('int32'))
  186.                 right   = min(image.size[0], np.floor(right).astype('int32'))
  187.                
  188.                 dir_save_path = "img_crop"
  189.                 if not os.path.exists(dir_save_path):
  190.                     os.makedirs(dir_save_path)
  191.                 crop_image = image.crop([left, top, right, bottom])
  192.                 crop_image.save(os.path.join(dir_save_path, "crop_" + str(i) + ".png"), quality=95, subsampling=0)
  193.                 print("save crop_" + str(i) + ".png to " + dir_save_path)
  194.         #---------------------------------------------------------#
  195.         #   图像绘制
  196.         #---------------------------------------------------------#
  197.         for i, c in list(enumerate(top_label)):
  198.             predicted_class = self.class_names[int(c)]
  199.             box             = top_boxes[i]
  200.             score           = top_conf[i]
  201.             top, left, bottom, right = box
  202.             top     = max(0, np.floor(top).astype('int32'))
  203.             left    = max(0, np.floor(left).astype('int32'))
  204.             bottom  = min(image.size[1], np.floor(bottom).astype('int32'))
  205.             right   = min(image.size[0], np.floor(right).astype('int32'))
  206.             label = '{} {:.2f}'.format(predicted_class, score)
  207.             draw = ImageDraw.Draw(image)
  208.             label_size = draw.textsize(label, font)
  209.             label = label.encode('utf-8')
  210.             print(label, top, left, bottom, right)
  211.             
  212.             if top - label_size[1] >= 0:
  213.                 text_origin = np.array([left, top - label_size[1]])
  214.             else:
  215.                 text_origin = np.array([left, top + 1])
  216.             for i in range(thickness):
  217.                 draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c])
  218.             draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c])
  219.             draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font)
  220.             del draw
  221.         return image
  222.     def get_FPS(self, image, test_interval):
  223.         #---------------------------------------------------#
  224.         #   计算输入图片的高和宽
  225.         #---------------------------------------------------#
  226.         image_shape = np.array(np.shape(image)[0:2])
  227.         #---------------------------------------------------------#
  228.         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。
  229.         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB
  230.         #---------------------------------------------------------#
  231.         image       = cvtColor(image)
  232.         #---------------------------------------------------------#
  233.         #   给图像增加灰条,实现不失真的resize
  234.         #   也可以直接resize进行识别
  235.         #---------------------------------------------------------#
  236.         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image)
  237.         #---------------------------------------------------------#
  238.         #   添加上batch_size维度,图片预处理,归一化。
  239.         #---------------------------------------------------------#
  240.         image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0)
  241.         with torch.no_grad():
  242.             #---------------------------------------------------#
  243.             #   转化成torch的形式
  244.             #---------------------------------------------------#
  245.             images = torch.from_numpy(image_data).type(torch.FloatTensor)
  246.             if self.cuda:
  247.                 images = images.cuda()
  248.             #---------------------------------------------------------#
  249.             #   将图像输入网络当中进行预测!
  250.             #---------------------------------------------------------#
  251.             outputs     = self.net(images)
  252.             #-----------------------------------------------------------#
  253.             #   将预测结果进行解码
  254.             #-----------------------------------------------------------#
  255.             results     = self.bbox_util.decode_box(outputs, self.anchors, image_shape, self.input_shape, self.letterbox_image,
  256.                                                     nms_iou = self.nms_iou, confidence = self.confidence)
  257.         t1 = time.time()
  258.         for _ in range(test_interval):
  259.             with torch.no_grad():
  260.                 #---------------------------------------------------------#
  261.                 #   将图像输入网络当中进行预测!
  262.                 #---------------------------------------------------------#
  263.                 outputs     = self.net(images)
  264.                 #-----------------------------------------------------------#
  265.                 #   将预测结果进行解码
  266.                 #-----------------------------------------------------------#
  267.                 results     = self.bbox_util.decode_box(outputs, self.anchors, image_shape, self.input_shape, self.letterbox_image,
  268.                                                         nms_iou = self.nms_iou, confidence = self.confidence)
  269.         t2 = time.time()
  270.         tact_time = (t2 - t1) / test_interval
  271.         return tact_time
  272.     def convert_to_onnx(self, simplify, model_path):
  273.         import onnx
  274.         self.generate(onnx=True)
  275.         im                  = torch.zeros(1, 3, *self.input_shape).to('cpu')  # image size(1, 3, 512, 512) BCHW
  276.         input_layer_names   = ["images"]
  277.         output_layer_names  = ["output"]
  278.         
  279.         # Export the model
  280.         print(f'Starting export with onnx {onnx.__version__}.')
  281.         torch.onnx.export(self.net,
  282.                         im,
  283.                         f               = model_path,
  284.                         verbose         = False,
  285.                         opset_version   = 12,
  286.                         training        = torch.onnx.TrainingMode.EVAL,
  287.                         do_constant_folding = True,
  288.                         input_names     = input_layer_names,
  289.                         output_names    = output_layer_names,
  290.                         dynamic_axes    = None)
  291.         # Checks
  292.         model_onnx = onnx.load(model_path)  # load onnx model
  293.         onnx.checker.check_model(model_onnx)  # check onnx model
  294.         # Simplify onnx
  295.         if simplify:
  296.             import onnxsim
  297.             print(f'Simplifying with onnx-simplifier {onnxsim.__version__}.')
  298.             model_onnx, check = onnxsim.simplify(
  299.                 model_onnx,
  300.                 dynamic_input_shape=False,
  301.                 input_shapes=None)
  302.             assert check, 'assert check failed'
  303.             onnx.save(model_onnx, model_path)
  304.         print('Onnx model save as {}'.format(model_path))
  305.    
  306.     def get_map_txt(self, image_id, image, class_names, map_out_path):
  307.         f = open(os.path.join(map_out_path, "detection-results/"+image_id+".txt"),"w")
  308.         #---------------------------------------------------#
  309.         #   计算输入图片的高和宽
  310.         #---------------------------------------------------#
  311.         image_shape = np.array(np.shape(image)[0:2])
  312.         #---------------------------------------------------------#
  313.         #   在这里将图像转换成RGB图像,防止灰度图在预测时报错。
  314.         #   代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB
  315.         #---------------------------------------------------------#
  316.         image       = cvtColor(image)
  317.         #---------------------------------------------------------#
  318.         #   给图像增加灰条,实现不失真的resize
  319.         #   也可以直接resize进行识别
  320.         #---------------------------------------------------------#
  321.         image_data  = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image)
  322.         #---------------------------------------------------------#
  323.         #   添加上batch_size维度,图片预处理,归一化。
  324.         #---------------------------------------------------------#
  325.         image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0)
  326.         with torch.no_grad():
  327.             #---------------------------------------------------#
  328.             #   转化成torch的形式
  329.             #---------------------------------------------------#
  330.             images = torch.from_numpy(image_data).type(torch.FloatTensor)
  331.             if self.cuda:
  332.                 images = images.cuda()
  333.             #---------------------------------------------------------#
  334.             #   将图像输入网络当中进行预测!
  335.             #---------------------------------------------------------#
  336.             outputs     = self.net(images)
  337.             #-----------------------------------------------------------#
  338.             #   将预测结果进行解码
  339.             #-----------------------------------------------------------#
  340.             results     = self.bbox_util.decode_box(outputs, self.anchors, image_shape, self.input_shape, self.letterbox_image,
  341.                                                     nms_iou = self.nms_iou, confidence = self.confidence)
  342.             #--------------------------------------#
  343.             #   如果没有检测到物体,则返回原图
  344.             #--------------------------------------#
  345.             if len(results[0]) <= 0:
  346.                 return
  347.             top_label   = np.array(results[0][:, 4], dtype = 'int32')
  348.             top_conf    = results[0][:, 5]
  349.             top_boxes   = results[0][:, :4]
  350.         
  351.         for i, c in list(enumerate(top_label)):
  352.             predicted_class = self.class_names[int(c)]
  353.             box             = top_boxes[i]
  354.             score           = str(top_conf[i])
  355.             top, left, bottom, right = box
  356.             if predicted_class not in class_names:
  357.                 continue
  358.             f.write("%s %s %s %s %s %s\n" % (predicted_class, score[:6], str(int(left)), str(int(top)), str(int(right)),str(int(bottom))))
  359.         f.close()
  360.         return
复制代码
采用图片进行猜测:
  1. #-----------------------------------------------------------------------#
  2. #   predict.py将单张图片预测、摄像头检测、FPS测试和目录遍历检测等功能
  3. #   整合到了一个py文件中,通过指定mode进行模式的修改。
  4. #-----------------------------------------------------------------------#
  5. import time
  6. import cv2
  7. import numpy as np
  8. from PIL import Image
  9. from matplotlib import pyplot as plt
  10. from ssd import SSD
  11. if __name__ == "__main__":
  12.     ssd = SSD()
  13.     #----------------------------------------------------------------------------------------------------------#
  14.     #   mode用于指定测试的模式:
  15.     #   'predict'           表示单张图片预测,如果想对预测过程进行修改,如保存图片,截取对象等,可以先看下方详细的注释
  16.     #   'video'             表示视频检测,可调用摄像头或者视频进行检测,详情查看下方注释。
  17.     #   'fps'               表示测试fps,使用的图片是img里面的street.jpg,详情查看下方注释。
  18.     #   'dir_predict'       表示遍历文件夹进行检测并保存。默认遍历img文件夹,保存img_out文件夹,详情查看下方注释。
  19.     #   'export_onnx'       表示将模型导出为onnx,需要pytorch1.7.1以上。
  20.     #----------------------------------------------------------------------------------------------------------#
  21.     mode = "predict"
  22.     #-------------------------------------------------------------------------#
  23.     #   crop                指定了是否在单张图片预测后对目标进行截取
  24.     #   count               指定了是否进行目标的计数
  25.     #   crop、count仅在mode='predict'时有效
  26.     #-------------------------------------------------------------------------#
  27.     crop            = False
  28.     count           = False
  29.     #----------------------------------------------------------------------------------------------------------#
  30.     #   video_path          用于指定视频的路径,当video_path=0时表示检测摄像头
  31.     #                       想要检测视频,则设置如video_path = "xxx.mp4"即可,代表读取出根目录下的xxx.mp4文件。
  32.     #   video_save_path     表示视频保存的路径,当video_save_path=""时表示不保存
  33.     #                       想要保存视频,则设置如video_save_path = "yyy.mp4"即可,代表保存为根目录下的yyy.mp4文件。
  34.     #   video_fps           用于保存的视频的fps
  35.     #
  36.     #   video_path、video_save_path和video_fps仅在mode='video'时有效
  37.     #   保存视频时需要ctrl+c退出或者运行到最后一帧才会完成完整的保存步骤。
  38.     #----------------------------------------------------------------------------------------------------------#
  39.     video_path      = 0
  40.     video_save_path = ""
  41.     video_fps       = 25.0
  42.     #----------------------------------------------------------------------------------------------------------#
  43.     #   test_interval       用于指定测量fps的时候,图片检测的次数。理论上test_interval越大,fps越准确。
  44.     #   fps_image_path      用于指定测试的fps图片
  45.     #   
  46.     #   test_interval和fps_image_path仅在mode='fps'有效
  47.     #----------------------------------------------------------------------------------------------------------#
  48.     test_interval = 100
  49.     fps_image_path = "img/"
  50.     #-------------------------------------------------------------------------#
  51.     #   dir_origin_path     指定了用于检测的图片的文件夹路径
  52.     #   dir_save_path       指定了检测完图片的保存路径
  53.     #   
  54.     #   dir_origin_path和dir_save_path仅在mode='dir_predict'时有效
  55.     #-------------------------------------------------------------------------#
  56.     dir_origin_path = "img/"
  57.     dir_save_path = "img_out/"
  58.     #-------------------------------------------------------------------------#
  59.     #   simplify            使用Simplify onnx
  60.     #   onnx_save_path      指定了onnx的保存路径
  61.     #-------------------------------------------------------------------------#
  62.     simplify        = True
  63.     onnx_save_path  = "model_data/models.onnx"
  64.    
  65.     if mode == "predict":
  66.         '''
  67.         1、如果想要进行检测完的图片的保存,利用r_image.save("img.jpg")即可保存,直接在predict.py里进行修改即可。
  68.         2、如果想要获得预测框的坐标,可以进入ssd.detect_image函数,在绘图部分读取top,left,bottom,right这四个值。
  69.         3、如果想要利用预测框截取下目标,可以进入ssd.detect_image函数,在绘图部分利用获取到的top,left,bottom,right这四个值
  70.         在原图上利用矩阵的方式进行截取。
  71.         4、如果想要在预测图上写额外的字,比如检测到的特定目标的数量,可以进入ssd.detect_image函数,在绘图部分对predicted_class进行判断,
  72.         比如判断if predicted_class == 'car': 即可判断当前目标是否为车,然后记录数量即可。利用draw.text即可写字。
  73.         '''
  74.         while True:
  75.             # img = input('Input image filename:')
  76.             # try:
  77.             #     image = Image.open(img)
  78.             # except:
  79.             #     print('Open Error! Try again!')
  80.             #     continue
  81.             # else:
  82.             #     r_image = ssd.detect_image(image, crop = crop, count=count)
  83.             #     r_image.show()
  84.             image = Image.open("img/street.jpg")
  85.             plt.imshow(image)
  86.             plt.show()
  87.             r_image = ssd.detect_image(image, crop=crop, count=count)
  88.             plt.imshow(r_image)
  89.             plt.show()
  90.     elif mode == "video":
  91.         capture = cv2.VideoCapture(video_path)
  92.         if video_save_path != "":
  93.             fourcc = cv2.VideoWriter_fourcc(*'XVID')
  94.             size = (int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
  95.             out = cv2.VideoWriter(video_save_path, fourcc, video_fps, size)
  96.         ref, frame = capture.read()
  97.         if not ref:
  98.             raise ValueError("未能正确读取摄像头(视频),请注意是否正确安装摄像头(是否正确填写视频路径)。")
  99.         fps = 0.0
  100.         while(True):
  101.             t1 = time.time()
  102.             # 读取某一帧
  103.             ref, frame = capture.read()
  104.             if not ref:
  105.                 break
  106.             # 格式转变,BGRtoRGB
  107.             frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
  108.             # 转变成Image
  109.             frame = Image.fromarray(np.uint8(frame))
  110.             # 进行检测
  111.             frame = np.array(ssd.detect_image(frame))
  112.             # RGBtoBGR满足opencv显示格式
  113.             frame = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR)
  114.             
  115.             fps  = ( fps + (1./(time.time()-t1)) ) / 2
  116.             print("fps= %.2f"%(fps))
  117.             frame = cv2.putText(frame, "fps= %.2f"%(fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
  118.             
  119.             cv2.imshow("video",frame)
  120.             c= cv2.waitKey(1) & 0xff
  121.             if video_save_path!="":
  122.                 out.write(frame)
  123.             if c==27:
  124.                 capture.release()
  125.                 break
  126.         print("Video Detection Done!")
  127.         capture.release()
  128.         if video_save_path!="":
  129.             print("Save processed video to the path :" + video_save_path)
  130.             out.release()
  131.         cv2.destroyAllWindows()
  132.         
  133.     elif mode == "fps":
  134.         img = Image.open(fps_image_path)
  135.         tact_time = ssd.get_FPS(img, test_interval)
  136.         print(str(tact_time) + ' seconds, ' + str(1/tact_time) + 'FPS, @batch_size 1')
  137.     elif mode == "dir_predict":
  138.         import os
  139.         from tqdm import tqdm
  140.         img_names = os.listdir(dir_origin_path)
  141.         for img_name in tqdm(img_names):
  142.             if img_name.lower().endswith(('.bmp', '.dib', '.png', '.jpg', '.jpeg', '.pbm', '.pgm', '.ppm', '.tif', '.tiff')):
  143.                 image_path = os.path.join(dir_origin_path, img_name)
  144.                 image = Image.open(image_path)
  145.                 r_image = ssd.detect_image(image)
  146.                 if not os.path.exists(dir_save_path):
  147.                     os.makedirs(dir_save_path)
  148.                 r_image.save(os.path.join(dir_save_path, img_name.replace(".jpg", ".png")), quality=95, subsampling=0)
  149.     elif mode == "export_onnx":
  150.         ssd.convert_to_onnx(simplify, onnx_save_path)
  151.         
  152.     else:
  153.         raise AssertionError("Please specify the correct mode: 'predict', 'video', 'fps' or 'dir_predict'.")
复制代码
  代码猜测效果如下:
  

  • 输入图像

  

  • 猜测效果 

  

  
猜测的准确率照旧异常高的,但是猜测的时间照旧有点长。

  

二、YOLO

这次我紧张学习了YOLO-v1,由于YOLO是个很大的系列,我这次对其主体头脑进行了学习。SSD是在YOLO-v1之后出来的,比第一版本的YOLO效果好,但是SSD的作者后期没用继承对其优化,但是YOLO不停源源不停更新,如今以高精度、高速率广泛应用于各商业之中。
我们先来看看YOLO和SSD之间有何异同,两个模子网络结构对比图如下所示:

这里我们可以看出,YOLO在提取特性之后,直接进入猜测环节。相比于上面的SSD将特性图像分为6个维度,采用差别尺寸的anchor而言轻便了很多,轻便代表了更易于训练模子,但是准确度会不会仍然可以可能保持较高水准呢?让我们先来相知趣识YOLO的原理吧。

YOLO是将图像分为 7x7 的网格,每个网格就类似于感爱好地域。假如某个目标的中心在这个网格之中,则该网格负责猜测这个目标。 每个网格必要猜测B个bounding box,每个bounding box除了猜测目标框位置外,还必要附带confidence值,以及C个种别的猜测分数。

我们先看看YOLO模子的团体过程,继承 448x448 巨细的图像输入,颠末卷积、池化等使用得到 7x7x1024 的特性图像,将其展平后连接4096维的全连接层,得到4096维的特性向量。末了,在接入一个1470维(7x7x30)的全连接层后,将其reshape为 7x7x30 的特性图像,即上图结构中最右侧。
这里的 7x7x30 的特性图像对应了原图像 7x7=49 个网格,即每30维的特性向量代表着每个表格猜测值。由于这30不是凭空出现的,而是B=2,即猜测两个bounding box,每个bounding  box有4个值(x,y,w,h);以及一个confidence值,conference值代表猜测的目标框与真实框的交并比IoU。又由于VOC数据集种别为20类,以是c=20。于是,
。
用一张图片来资助明确,如下所示:

YOLO的bounding box猜测的坐标是直接猜测原图对应的位置,再加上整个网络没有使用anchor,直接将图像分为49个网格,以是速率特殊快,可以可能到达45FPS。也因此可想而知,一定捐躯一些准确率,比使用anchor的SSD准确率低。这也回应开篇的疑问,同时也暗示了YOLO系列后期的优化门路。 下周还会继承完成YOLO系列的学习。
末了,我们再来看看YOLO模子的丧失计算吧。

可能会迷惑在计算bounding box丧失时,为什么计算w和h时,必要先开根号。

由于假如两个巨细差别的框,但是w和h相差相同,假如不进行开根号使用,则计算出来的偏差是相同的。这显然不符合逻辑,由于
对应的框更小,纵然
,其相对于框的偏差偏移是更大的,以是必要先进行开根号使用,如上图左侧坐标所示。

三、Termius

英文必要频仍使用租用的服务器,但服务器管理又比力麻烦,上传数据也会消耗很多时间。于是,我学习使用了Termius,用以管理服务器数据,便于数据传输,同时团结PyCharm实现当地与远程服务器之间同步使用。

 起首,点击NEW HOST就可以创建一个新的服务器连接。

输入服务器ip地点,以及用户名、密码和端标语,就可以进行服务器连接了。
连接乐成之后,会主动进入服务器终端界面,如下图所示:

点击SFTP,便可进行服务器端和当地之间的数据传输界面,如下图所示:

直接拖已往,就能实现服务器端和当地直接的数据传输,大大进步了速率。
然后,确保您的PyCharm已通过SSH连接到远程服务器,然后通过下图的使用,就可以可能看到刚才通过Termius上传到远程服务器的代码和数据。

假如想同步到当地,在当地进行修改等使用,可以从服务器端将文件下载到当地,使用如下:

统统就大功告成啦,可以在当地调试,然后通过远程服务器的资源训练自己的模子了。
 
总结

本周的学习到此竣事,下周将学习YOLO系列更新最大的几个版本,以及如今使用最为广泛的版本,并乐成跑通其代码。
如有错误,请各位大佬提出,谢谢!
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表