当前位置:   article > 正文

基于YOLOv5的输电线路绝缘子缺陷检测项目_绝缘子缺陷数据集

绝缘子缺陷数据集

目录

1 项目背景

2 图像数据集介绍

3 模型训练部分

4 模型性能测试

1 项目背景

        随着输电网络规模不断增大,输电线路巡检任务日益加重,实现输电 线路的高效率巡检已刻不容缓。传统的巡检技术较为落后,主要依靠人工的方式,存在耗时长等问题,尤其对于复杂环境下的人工巡检,弊端更为凸显。对于输电线路而言,若不及时处理故障,将对电力系统的稳定运行产生不良影响。目前,基于深度学习的输电线路巡检方法逐步取代传统人工巡检,本项目基于YOLOv5算法,针对488幅绝缘子缺陷图像,开展其缺陷的智能检测和识别研究。

2 图像数据集介绍

     绝缘子破损会造成其绝缘性能的下降,若在巡检中发现应该及时更换。

      输电线路绝缘子缺陷检测图像数据集共包含绝缘子破损、闪络缺陷、防震锤、正常绝缘子四类目标,采用旋转的方式进行扩充,并利用labelimg对其进行标注。

缺陷名称正常绝缘子破损闪络防震锤
标签数202212602723421

      图像翻转扩充的python代码如下(需安装cv2和numpy)

  1. import cv2
  2. import numpy as np
  3. def show_image(image, labels, h, w, winname):
  4. image = image.copy().astype(np.uint8)
  5. for l in labels:
  6. x1 = int((l[1]-l[3]/2) * w)
  7. y1 = int((l[2]-l[4]/2) * h)
  8. x2 = int((l[1]+l[3]/2) * w)
  9. y2 = int((l[2]+l[4]/2) * h)
  10. # print(image.shape, (x1, y1), (x2, y2))
  11. cv2.rectangle(image, (x1, y1), (x2, y2), (0, 0, 255), 3)
  12. cv2.imshow(winname, image)
  13. if __name__ == '__main__':
  14. image0 = cv2.imread('lena.jpg')
  15. h, w = image0.shape[:2]
  16. labels0 = np.array([[0, 50., 50, 20, 20], [0, 150, 150, 30, 30]])
  17. labels0[:, 1::2] = labels0[:, 1::2] / w
  18. labels0[:, 2::2] = labels0[:, 2::2] / h
  19. image = image0.copy()
  20. show_image(image, labels0, h, w, 'raw img')
  21. #fliplr
  22. image = image0.copy()
  23. labels = labels0.copy()
  24. image = np.fliplr(image)
  25. labels[:, 1] = 1 - labels[:, 1]
  26. show_image(image, labels, h, w, 'fliplr')
  27. #flipud
  28. image = image0.copy()
  29. labels = labels0.copy()
  30. image = np.flipud(image)
  31. labels[:, 2] = 1 - labels[:, 2]
  32. show_image(image, labels, h, w, 'flipud')
  33. #rot90
  34. image = image0.copy()
  35. labels = labels0.copy()
  36. image = np.rot90(image)
  37. labels = labels[:, [0, 2, 1, 4, 3]]
  38. labels[:, 2] = 1 - labels[:, 2]
  39. show_image(image, labels, h, w, 'rot90')
  40. #rot180
  41. image = image0.copy()
  42. labels = labels0.copy()
  43. image = np.rot90(image)
  44. image = np.rot90(image)
  45. labels[:, 1:3] = 1 - labels[:, 1:3]
  46. show_image(image, labels, h, w, 'rot180')
  47. #rot270
  48. image = image0.copy()
  49. labels = labels0.copy()
  50. image = np.rot90(image)
  51. image = np.rot90(image)
  52. image = np.rot90(image)
  53. labels = labels[:, [0, 2, 1, 4, 3]]
  54. labels[:, 1] = 1 - labels[:, 1]
  55. show_image(image, labels, h, w, 'rot270')
  56. cv2.waitKey(0)

3 模型训练部分

      YOLOv5 模型是 Ultralytics 公司于 2020 年 6 月 9 日公开发布的。YOLOv5 模型是基于 YOLOv3 模型基础上改进而来的,有 YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x 四个模型。YOLOv5 模型由骨干网络、颈部和头部组成。

3.1 骨干特征提取网络backbone

        YOLOv5 模型的骨干网络主要由 Focus、BottleneckCSP 和 SSP 网络构成,其中主要包括 Focus、Conv 卷积块、BottleneckCSP 和 SSP 等模块。

  1. from functools import wraps
  2. import tensorflow as tf
  3. from tensorflow.keras import backend as K
  4. from tensorflow.keras.initializers import RandomNormal
  5. from tensorflow.keras.layers import (Add, BatchNormalization, Concatenate,
  6. Conv2D, Layer, MaxPooling2D,
  7. ZeroPadding2D)
  8. from tensorflow.keras.regularizers import l2
  9. from utils.utils import compose
  10. class SiLU(Layer):
  11. def __init__(self, **kwargs):
  12. super(SiLU, self).__init__(**kwargs)
  13. self.supports_masking = True
  14. def call(self, inputs):
  15. return inputs * K.sigmoid(inputs)
  16. def get_config(self):
  17. config = super(SiLU, self).get_config()
  18. return config
  19. def compute_output_shape(self, input_shape):
  20. return input_shape
  21. class Focus(Layer):
  22. def __init__(self):
  23. super(Focus, self).__init__()
  24. def compute_output_shape(self, input_shape):
  25. return (input_shape[0], input_shape[1] // 2 if input_shape[1] != None else input_shape[1], input_shape[2] // 2 if input_shape[2] != None else input_shape[2], input_shape[3] * 4)
  26. def call(self, x):
  27. return tf.concat(
  28. [x[..., ::2, ::2, :],
  29. x[..., 1::2, ::2, :],
  30. x[..., ::2, 1::2, :],
  31. x[..., 1::2, 1::2, :]],
  32. axis=-1
  33. )
  34. @wraps(Conv2D)
  35. def DarknetConv2D(*args, **kwargs):
  36. darknet_conv_kwargs = {'kernel_initializer' : RandomNormal(stddev=0.02), 'kernel_regularizer' : l2(kwargs.get('weight_decay', 5e-4))}
  37. darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2, 2) else 'same'
  38. try:
  39. del kwargs['weight_decay']
  40. except:
  41. pass
  42. darknet_conv_kwargs.update(kwargs)
  43. return Conv2D(*args, **darknet_conv_kwargs)
  44. def DarknetConv2D_BN_SiLU(*args, **kwargs):
  45. no_bias_kwargs = {'use_bias': False}
  46. no_bias_kwargs.update(kwargs)
  47. if "name" in kwargs.keys():
  48. no_bias_kwargs['name'] = kwargs['name'] + '.conv'
  49. return compose(
  50. DarknetConv2D(*args, **no_bias_kwargs),
  51. BatchNormalization(momentum = 0.97, epsilon = 0.001, name = kwargs['name'] + '.bn'),
  52. SiLU())
  53. def Bottleneck(x, out_channels, shortcut=True, weight_decay=5e-4, name = ""):
  54. y = compose(
  55. DarknetConv2D_BN_SiLU(out_channels, (1, 1), weight_decay=weight_decay, name = name + '.cv1'),
  56. DarknetConv2D_BN_SiLU(out_channels, (3, 3), weight_decay=weight_decay, name = name + '.cv2'))(x)
  57. if shortcut:
  58. y = Add()([x, y])
  59. return y
  60. def C3(x, num_filters, num_blocks, shortcut=True, expansion=0.5, weight_decay=5e-4, name=""):
  61. hidden_channels = int(num_filters * expansion)
  62. x_1 = DarknetConv2D_BN_SiLU(hidden_channels, (1, 1), weight_decay=weight_decay, name = name + '.cv1')(x)
  63. x_2 = DarknetConv2D_BN_SiLU(hidden_channels, (1, 1), weight_decay=weight_decay, name = name + '.cv2')(x)
  64. for i in range(num_blocks):
  65. x_1 = Bottleneck(x_1, hidden_channels, shortcut=shortcut, weight_decay=weight_decay, name = name + '.m.' + str(i))
  66. route = Concatenate()([x_1, x_2])
  67. return DarknetConv2D_BN_SiLU(num_filters, (1, 1), weight_decay=weight_decay, name = name + '.cv3')(route)
  68. def SPPBottleneck(x, out_channels, weight_decay=5e-4, name = ""):
  69. #---------------------------------------------------#
  70. #---------------------------------------------------#
  71. x = DarknetConv2D_BN_SiLU(out_channels // 2, (1, 1), weight_decay=weight_decay, name = name + '.cv1')(x)
  72. maxpool1 = MaxPooling2D(pool_size=(5, 5), strides=(1, 1), padding='same')(x)
  73. maxpool2 = MaxPooling2D(pool_size=(9, 9), strides=(1, 1), padding='same')(x)
  74. maxpool3 = MaxPooling2D(pool_size=(13, 13), strides=(1, 1), padding='same')(x)
  75. x = Concatenate()([x, maxpool1, maxpool2, maxpool3])
  76. x = DarknetConv2D_BN_SiLU(out_channels, (1, 1), weight_decay=weight_decay, name = name + '.cv2')(x)
  77. return x
  78. def resblock_body(x, num_filters, num_blocks, expansion=0.5, shortcut=True, last=False, weight_decay=5e-4, name = ""):
  79. #----------------------------------------------------------------#
  80. #----------------------------------------------------------------#
  81. # 320, 320, 64 => 160, 160, 128
  82. x = ZeroPadding2D(((1, 0),(1, 0)))(x)
  83. x = DarknetConv2D_BN_SiLU(num_filters, (3, 3), strides = (2, 2), weight_decay=weight_decay, name = name + '.0')(x)
  84. if last:
  85. x = SPPBottleneck(x, num_filters, weight_decay=weight_decay, name = name + '.1')
  86. return C3(x, num_filters, num_blocks, shortcut=shortcut, expansion=expansion, weight_decay=weight_decay, name = name + '.1' if not last else name + '.2')
  87. -----------------------#
  88. def darknet_body(x, base_channels, base_depth, weight_decay=5e-4):
  89. # 640, 640, 3 => 320, 320, 12
  90. x = Focus()(x)
  91. # 320, 320, 12 => 320, 320, 64
  92. x = DarknetConv2D_BN_SiLU(base_channels, (3, 3), weight_decay=weight_decay, name = 'backbone.stem.conv')(x)
  93. # 320, 320, 64 => 160, 160, 128
  94. x = resblock_body(x, base_channels * 2, base_depth, weight_decay=weight_decay, name = 'backbone.dark2')
  95. # 160, 160, 128 => 80, 80, 256
  96. x = resblock_body(x, base_channels * 4, base_depth * 3, weight_decay=weight_decay, name = 'backbone.dark3')
  97. feat1 = x
  98. # 80, 80, 256 => 40, 40, 512
  99. x = resblock_body(x, base_channels * 8, base_depth * 3, weight_decay=weight_decay, name = 'backbone.dark4')
  100. feat2 = x
  101. # 40, 40, 512 => 20, 20, 1024
  102. x = resblock_body(x, base_channels * 16, base_depth, shortcut=False, last=True, weight_decay=weight_decay, name = 'backbone.dark5')
  103. feat3 = x
  104. return feat1,feat2,feat3

3.2 颈部neck

      YOLOv5 使用的 BottleneckCSP1 和 BottleneckCSP2 能在保证准确的同时,提高网络速度。YOLOv5 的颈部采用 BottleneckCSP2 模块减少模型参数量,通过上采样操作 80*80*512 大小的特征图,上采样过程由 2 组 BottleneckCSP2、大小为 1 步长为 1的 Conv、Upsample 和 Concat 连接完成。

  1. from tensorflow.keras.layers import (Concatenate, Input, Lambda, UpSampling2D,
  2. ZeroPadding2D)
  3. from tensorflow.keras.models import Model
  4. from nets.CSPdarknet import (C3, DarknetConv2D, DarknetConv2D_BN_SiLU,
  5. darknet_body)
  6. from nets.yolo_training import yolo_loss
  7. #---------------------------------------------------#
  8. def yolo_body(input_shape, anchors_mask, num_classes, phi, weight_decay=5e-4):
  9. depth_dict = {'s' : 0.33, 'm' : 0.67, 'l' : 1.00, 'x' : 1.33,}
  10. width_dict = {'s' : 0.50, 'm' : 0.75, 'l' : 1.00, 'x' : 1.25,}
  11. dep_mul, wid_mul = depth_dict[phi], width_dict[phi]
  12. base_channels = int(wid_mul * 64) # 64
  13. base_depth = max(round(dep_mul * 3), 1) # 3
  14. inputs = Input(input_shape)
  15. #---------------------------------------------------#
  16. #---------------------------------------------------#
  17. feat1, feat2, feat3 = darknet_body(inputs, base_channels, base_depth, weight_decay)
  18. P5 = DarknetConv2D_BN_SiLU(int(base_channels * 8), (1, 1), weight_decay=weight_decay, name = 'conv_for_feat3')(feat3)
  19. P5_upsample = UpSampling2D()(P5)
  20. P5_upsample = Concatenate(axis = -1)([P5_upsample, feat2])
  21. P5_upsample = C3(P5_upsample, int(base_channels * 8), base_depth, shortcut = False, weight_decay=weight_decay, name = 'conv3_for_upsample1')
  22. P4 = DarknetConv2D_BN_SiLU(int(base_channels * 4), (1, 1), weight_decay=weight_decay, name = 'conv_for_feat2')(P5_upsample)
  23. P4_upsample = UpSampling2D()(P4)
  24. P4_upsample = Concatenate(axis = -1)([P4_upsample, feat1])
  25. P3_out = C3(P4_upsample, int(base_channels * 4), base_depth, shortcut = False, weight_decay=weight_decay, name = 'conv3_for_upsample2')
  26. P3_downsample = ZeroPadding2D(((1, 0),(1, 0)))(P3_out)
  27. P3_downsample = DarknetConv2D_BN_SiLU(int(base_channels * 4), (3, 3), strides = (2, 2), weight_decay=weight_decay, name = 'down_sample1')(P3_downsample)
  28. P3_downsample = Concatenate(axis = -1)([P3_downsample, P4])
  29. P4_out = C3(P3_downsample, int(base_channels * 8), base_depth, shortcut = False, weight_decay=weight_decay, name = 'conv3_for_downsample1')
  30. P4_downsample = ZeroPadding2D(((1, 0),(1, 0)))(P4_out)
  31. P4_downsample = DarknetConv2D_BN_SiLU(int(base_channels * 8), (3, 3), strides = (2, 2), weight_decay=weight_decay, name = 'down_sample2')(P4_downsample)
  32. P4_downsample = Concatenate(axis = -1)([P4_downsample, P5])
  33. P5_out = C3(P4_downsample, int(base_channels * 16), base_depth, shortcut = False, weight_decay=weight_decay, name = 'conv3_for_downsample2')
  34. out2 = DarknetConv2D(len(anchors_mask[2]) * (5 + num_classes), (1, 1), strides = (1, 1), weight_decay=weight_decay, name = 'yolo_head_P3')(P3_out)
  35. out1 = DarknetConv2D(len(anchors_mask[1]) * (5 + num_classes), (1, 1), strides = (1, 1), weight_decay=weight_decay, name = 'yolo_head_P4')(P4_out)
  36. out0 = DarknetConv2D(len(anchors_mask[0]) * (5 + num_classes), (1, 1), strides = (1, 1), weight_decay=weight_decay, name = 'yolo_head_P5')(P5_out)
  37. return Model(inputs, [out0, out1, out2])
  38. def get_train_model(model_body, input_shape, num_classes, anchors, anchors_mask, label_smoothing):
  39. y_true = [Input(shape = (input_shape[0] // {0:32, 1:16, 2:8}[l], input_shape[1] // {0:32, 1:16, 2:8}[l], \
  40. len(anchors_mask[l]), num_classes + 5)) for l in range(len(anchors_mask))]
  41. model_loss = Lambda(
  42. yolo_loss,
  43. output_shape = (1, ),
  44. name = 'yolo_loss',
  45. arguments = {
  46. 'input_shape' : input_shape,
  47. 'anchors' : anchors,
  48. 'anchors_mask' : anchors_mask,
  49. 'num_classes' : num_classes,
  50. 'label_smoothing' : label_smoothing,
  51. 'balance' : [0.4, 1.0, 4],
  52. 'box_ratio' : 0.05,
  53. 'obj_ratio' : 1 * (input_shape[0] * input_shape[1]) / (640 ** 2),
  54. 'cls_ratio' : 0.5 * (num_classes / 80)
  55. }
  56. )([*model_body.output, *y_true])
  57. model = Model([model_body.input, *y_true], model_loss)
  58. return model

3.3 头部 YOLO head

        YOLOv5 的头部采用多尺度特征图用于检测,用大图像检测小目标,小图像检测大目标。对颈部三种不同尺度特征图,通过 Conv2d 卷积操作,最终得到三个大小分别为 80*80*255、40*40*255、20*20*255 的特征图。

3.4 输出层output

         在三个不同尺度特征图上生成候选框,由于本文对绝缘子缺陷的检测有 4 类,对 YOLOv5 的 3 个尺度特征图使用 3 种大小不同的候选框对四类目标进行预测,最后输出采用加权非极大值的方式对目标框进行筛选对生成的候选框进行筛选,输出目标分类和边框回归。

YOLOv5模型简易结构

        模型训练过程中采用余弦退火衰减算法、并采用adam优化完成对模型权值参数的更新,其中训练集:测试集=9:1,冻结原始预训练YOLOv5模型前234层,迭代训练100个epoch,batchsize1=8,再解冻迭代训练200个epoch,batchsize2=4,3060ti显卡。

4 模型性能测试

缺陷名称正常绝缘子破损闪络防震锤
AP值97.00%75.00%81.00%80.00%

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号