提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、YOLO是什么?
YOLO 是基于 的一种工具,该工具是为了解决数据分析任务而创建的。
二、使用步骤YOLO进行变电站缺陷检测
1.训练变电站缺陷数据集
根据国家电网的规范,变电站缺陷检测的类别包括如下:
包含如下缺陷类别:
表盘模糊
表盘破损
外壳破损
表计读数有误
绝缘子破裂
地面油污
硅胶变色
硅胶体破损
箱门闭合异常
高空悬浮物
鸟巢
盖板破损
未带安全帽
未穿工装
吸烟
ywzt_yfyc
kgg_ybh
``每个类别样本约500张图像,数据集共8000张高质量标注的电站缺陷图像。
#变电站缺陷数据集联系方式为aricojf@163.com
weightConfiFile = "./config/weights/PowerStationDefectModelConfig.yaml" # 国网电站缺陷检测模型配置文件
modelConfig = Global.configData.models["PowerStationDefectModelConfig"] # 国网电站缺陷检测模型配置属性,临时没有用,但这样可以取出配置
modelManager = ModelManager(modelConfig)
modelManager.initModel()
imgsz = modelConfig.imgsz
confThres = modelConfig.confThres
targetFPS = Global.configData.coreConfig.targetFPS
# 线程方式
# streamReadServer = LoadStreams(ROOT / "stream.txt", img_size=imgsz, stride=stride, auto=pt, vid_stride=1)
# bs = len(streamReadServer)
# videoNodeManager.setWHFS(streamReadServer.sources,streamReadServer.width,streamReadServer.height,streamReadServer.fps)
# 进程方式
streamReadServer = NBStreamReadServer(__interruptedAI__, ROOT / "stream.txt", targetFPS=targetFPS,
enableLetterBox=True,
img_size=imgsz, stride=modelManager.model.stride, auto=modelManager.model.pt,
videoNodeManager=videoNodeManager)
if __interruptedAI__.value:
# 如果启动时,流读取进程无法连接源而处于不断尝试状态时,后来收到接口关闭AI消息,从而被打断,则退出推理进程
print(f'AI Inference Exit!')
torch.cuda.empty_cache()
__aiStatus__.value = AIServerRunningStatus.SERVER_STATUS_STOP.value
stopViewCV2ImgServer()
return
streamReadServer.start()
bs = len(streamReadServer)
# 6.Run inference
modelManager.model.warmup(imgsz=(1 if modelManager.model.pt else bs, 3, *imgsz)) # warmup
dt, seen = [0.0, 0.0, 0.0], 0
2.验证变电站缺陷检测数据集
代码如下(示例):
#变电站缺陷数据集联系方式为aricojf@163.com
#!/usr/bin/python3.9
# -*- coding: utf-8 -*-
# @Time : 2023/5/11 上午8:33
# @Author : aricochen
# @Email : aricojf@163.com
# @File : httpMain.py
# @Software: PyCharm
"""
国家电网智能分析接口实现。
本实现基于国家电网智能分析规范实现。<<500(330)千伏及以上变电站远程智能巡视系统技术规范(试行).pdf>>
本代码目前基于规范实现了电站缺陷检测。
#!/usr/bin/python3.9
# -*- coding: utf-8 -*-
# @Time : 2023/5/11 上午8:33
# @Author : aricochen
# @Email : aricojf@163.com
# @File : httpMain.py
# @Software: PyCharm
"""
from aricojf.common import Global
import numpy as np
import torch
import os
from pathlib import Path
from utils.general import (non_max_suppression, scale_boxes)
from utils.plots import Annotator, colors, save_one_box
import cv2
from pathlib import Path
import torch
import time
from flask import Flask, jsonify, request, abort, render_template
from easydict import EasyDict
import json
import requests
import uuid
import queue
import threading
from ftplib import FTP_TLS, FTP
from urllib.parse import urlparse
from aricojf.common.ConfigManager import updateModelWeightConfig, updateConfigKeyData
from aricojf.common.Logging import getLogger
logger = getLogger()
# =============================配置信息Begin=================================
# 国家电网,智能分析主机规则配置文件
powerStationDefectConfig = Global.configData.rules["PowerStationDefectRuleConfig"]
# 全局数据保存根目录
dataRoot = Global.configData.coreConfig.dataRoot
logger.info(f'全局数据保存根目录(项目根目录下的相对目录):{dataRoot}')
# --推理结果保存目录(相对项目根目录)
rsSaveDir = powerStationDefectConfig.rsSaveDir
logger.info(f'推理结果保存目录(相对全局数据保存根目录):{rsSaveDir}')
# http推理生成的图像保存目录
httpInferenceImagesDir = os.path.join(os.getcwd(), dataRoot, rsSaveDir)
logger.info(f'推理结果保存目录绝对目录:{httpInferenceImagesDir}')
if not Path(httpInferenceImagesDir).exists():
Path(httpInferenceImagesDir).mkdir(parents=True)
# 生成推理结果后本机httpServer图像的相对URL
inferenceRelativeURL = powerStationDefectConfig.inferenceRelativeURL
app = Flask(__name__, static_folder=httpInferenceImagesDir, static_url_path=inferenceRelativeURL)
logger.info(f'Server Static目录:{httpInferenceImagesDir}')
logger.info(f'Server static url:{inferenceRelativeURL}')
# =============================配置信息End=================================
# ===========================国网图像缺陷分析报文模板Begin===========================================
# 1.三方请求图像分析报文格式 N.3.1
picAnalyseTemp = {
"requestHostIp": "127.0.0.1",
"requestHostPort": 20011,
"requestId": "110",
"objectList": [
{
"objectId": "123",
"typeList": [],
"imageNormalUrlPath": "",
"imageUrlList": [
"http://localhost:20011/defectImages/test/biandian_05293.jpg",
"http://localhost:20011/defectImages/test/biandian_05294.jpg",
"http://localhost:20011/defectImages/test/biandian_05295.jpg",
"http://localhost:20011/defectImages/test/biandian_05296.jpg",
"http://localhost:20011/defectImages/test/biandian_05297.jpg",
"http://localhost:20011/defectImages/test/biandian_05298.jpg",
"http://localhost:20011/defectImages/test/biandian_05300.jpg"
]
}
]
}
# 2.本系统分析结果反馈报文格式 N.3.2
picAnalyseRetNotifyTemp = {
"requestId": "110",
"resultsList": [
{
"objectId": "123",
"type": [],
"value": [],
"code": "",
"resImageUrl": "",
"pos": [],
"conf": [],
"desc": ""
}
]
}
# 3.三方请求算法更新报文格式 N.3.3
algorithmUpdateTemp = {
"requestHostIp": "127.0.0.1",
"requestHostPort": 20011,
"requestId": "120",
"algorithmPath": "ftp://aricochen:chendeben@aricojfServer:21/home/aricochen/mask.txt"
}
# ===========================国网图像缺陷分析报文模板End===========================================
# =======================================================================
# 全局变量
model = None
rsServer = None
modelManager = None
httpInferCount = 0
modelStatus = 1 # 模型状态,1:正常,0:更新中,不能推理
class RSPostServer(threading.Thread):
def __init__(self, responseImgAnalysePortURL, responseAlgorithmUpdateURL, rsHttpServer, interruptedAI):
super().__init__()
self.responseImgAnalysePortURL = responseImgAnalysePortURL
self.responseAlgorithmUpdateURL = responseAlgorithmUpdateURL
self.rsHttpServer = rsHttpServer
self.rsQueue = queue.Queue()
self.__is_interrupted__ = interruptedAI
def putRS(self, rs):
self.rsQueue.put(rs)
def run(self):
"""
反馈缺陷检测到巡视主机,
规范文件章节: N.3.2
:return:
"""
while not self.__is_interrupted__.value:
action, rs = self.rsQueue.get()
requestHostIp = rs.requestHostIp
requestHostPort = rs.requestHostPort
requestId = rs.requestId
# ========1. n311:巡视主机发送图像识别请求处理======
try:
if action == "n311":
logger.info(f'推理三方的图像')
objectList = rs.objectList
responsePostURL = self.responseImgAnalysePortURL # 测试阶段使用
responsePostURL = "http://" + requestHostIp + ":" + str(requestHostPort) + "/picAnalyseRetNotify"
# 处理每一个obj
for idx, obj in enumerate(objectList):
logger.info(f'处理图像:{idx}')
objectId = obj.objectId
typeList = obj.typeList
imageUrlList = obj.imageUrlList
# imageNormalUrlPath = obj.imageNormalUrlPath 临时不用此字段
#处理每一幅图像
for imageURL in imageUrlList:
# 读取图像
logger.info(f' 读取图像:{imageURL}')
try:
image_bytes = requests.get(imageURL).content
image_np1 = np.frombuffer(image_bytes, dtype=np.uint8)
image_np2 = cv2.imdecode(image_np1, cv2.IMREAD_COLOR)
except Exception as e:
logger.error(e)
image_np2 =None
if image_np2 is None: # 图像读取错误
logger.error(f' 图像读取错误!')
data = EasyDict()
data.requestId = requestId
data.resultsList = []
obj = EasyDict()
obj.objectId = objectId
rs = EasyDict()
rs.type = ""
rs.value = ""
rs.code = "2001"
rs.resImageUrl = ""
rs.pos = []
rs.conf = []
rs.desc = []
obj.results = [rs]
data.resultsList.append(obj)
# 转换为json
jsonData = json.dumps(data)
# 发送识别反馈
headers = {
"Content-Type": "application/json; charset=UTF-8"
}
logger.info(f' 反馈三方错误!')
res = requests.post(responsePostURL, data=jsonData, headers=headers)
logger.info(f' 三方响应:{res}')
continue
else: # 图像读取正确,进行推理
logger.info(f' 图像读取SUCESS!')
imgIDXPath, imgURL, clsIDList, enNameList, zhNameList, xyxyList, confList = predictImg(
requestId,
image_np2,
typeList)
data = EasyDict()
data.requestId = requestId
data.resultList = []
obj = EasyDict()
obj.objectId = objectId
results = []
for idx, clsID in enumerate(clsIDList):
rs = EasyDict()
rs.type = enNameList[idx]
rs.value = ""
rs.code = "2000"
rs.resImageUrl = f"{self.rsHttpServer}{imgURL}"
rs.pos = [{"areas": [{"x": xyxyList[idx][0], "y": xyxyList[idx][1]},
{"x": xyxyList[idx][2], "y": xyxyList[idx][3]}]}]
rs.conf = confList[idx]
rs.desc = zhNameList[idx]
results.append(rs)
obj.results = results
data.resultList.append(obj)
# 转换为json
jsonData = json.dumps(data)
# 发送识别反馈
headers = {
"Content-Type": "application/json; charset=UTF-8"
}
logger.info(f' 反馈三方:{jsonData}')
res = requests.post(responsePostURL, data=jsonData, headers=headers)
logger.info(f' 三方响应:{res}')
except Exception as e:
logger.error(f'推理或推理结果发送三方反馈错误:{e}')
# ========2. n331:巡视主机发送算法更新请求处理======
try:
if action == "n331":
logger.info(f'处理三方发送模型更新请求...')
algorithmPath = rs.algorithmPath
responsePostURL = self.responseAlgorithmUpdateURL # 测试阶段使用
responsePostURL = "http://" + requestHostIp + ":" + str(requestHostPort) + "/algorithmUpdateResult"
ret, modelFile, isEncrypt = downloadWeight(algorithmPath)
if not ret: # download失败
logger.error(f'模型下载失败1:{modelFile}')
data = EasyDict()
data.requestId = requestId
data.result = "0"
# 转换为json
jsonData = json.dumps(data)
# 发送识别反馈
headers = {
"Content-Type": "application/json; charset=UTF-8"
}
logger.error(f' 反馈三方错误!')
res = requests.post(responsePostURL, data=jsonData, headers=headers)
logger.error(f' 三方响应:{res}')
return
logger.info(f' 模型下载成功!')
logger.info(f' 更新模型...')
ret, rs = updateModel(modelFile, isEncrypt) #模型下载成功,进行模型更新
# 反馈三方模型更新情况
data = EasyDict()
data.requestId = requestId
headers = {
"Content-Type": "application/json; charset=UTF-8"
}
if not ret:
logger.error(f' 更新模型失败:{rs}')
data.result = "0"
else: # 模型更新成功
logger.info(f' 更新模型SUCESS')
data.result = "1"
jsonData = json.dumps(data)
logger.info(f' 反馈三方...')
res = requests.post(responsePostURL, data=jsonData, headers=headers)
logger.info(f' 三方响应:{res}')
except Exception as e:
logger.error(f'模型升级或模型升级发送三方反馈错误:{e}')
def setHttpModel(_modelManager, interruptedAI):
global modelManager, model, device, rsServer
modelManager = _modelManager
model = modelManager.model
responseImgAnalysePortURL = powerStationDefectConfig.responseImgAnalysePortURL # 识别结果反馈三方URL
responseAlgorithmUpdateURL = powerStationDefectConfig.responseAlgorithmUpdateURL # 模型更新反馈三方URL
rsHttpServer = powerStationDefectConfig.rsHttpServer + powerStationDefectConfig.inferenceRelativeURL # 反馈给三方的可访问本地推理文件的相对地址
rsServer = RSPostServer(responseImgAnalysePortURL, responseAlgorithmUpdateURL, rsHttpServer,
interruptedAI=interruptedAI)
rsServer.start()
@app.route('/')
def helloPowerStationDefect():
return 'Hello!THIS IS ALGORITHM OF POWER STATION DEFECT by aricojf 2023.05'
@app.route('/mypost')
def mypost():
"""
页面模板测试
:return:
"""
return render_template("test.html")
# ==================本机模拟三方接口-->>接收本机反馈接口 Begin=========================
@app.route('/picAnalyseRetNotify', methods=["POST"])
def picAnalyseRetNotify():
"""
测试用,模拟推理结果反馈给三方url
:return:
"""
try:
get_Data = request.get_data()
# # 判断传入的json数据是否为空
if get_Data is None or get_Data == "":
logger.warn(f'收到非法数据')
return jsonify(code=400)
# 传入的参数为bytes类型,需要转化成json
get_Data = json.loads(get_Data)
data = EasyDict(get_Data)
logger.info(f'-----------------模拟三方接口:推理反馈结果------------------------')
logger.info(data)
return jsonify(code=200)
except Exception as e:
logger.error(f'三方模拟接口:推理反馈结果错误:{e}')
return jsonify(code=500)
@app.route('/algorithmUpdateResult', methods=["POST"])
def algorithmUpdateResult():
"""
测试用,模拟模型更新结果反馈给三方url
:return:
"""
try:
get_Data = request.get_data()
# # 判断传入的json数据是否为空
if get_Data is None or get_Data == "":
logger.warn(f'收到非法数据')
return jsonify(code=400)
# 传入的参数为bytes类型,需要转化成json
get_Data = json.loads(get_Data)
data = EasyDict(get_Data)
logger.info(f'-----------------模拟三方接口:模型更新反馈结果------------------------')
logger.info(data)
return jsonify(code=200)
except Exception as e:
logger.error(f'三方模拟接口:模型更新反馈结果错误:{e}')
return jsonify(code=500)
# ==================本机模拟三方接口-->>接收本机反馈接口 End=========================
# ====================本机提供的供三方request请求的接口 Begin========================
@app.route(powerStationDefectConfig.requestImgAnalyseRelaURL, methods=["POST"])
def picAnalyse():
"""
国家电网分析主机,分析缺陷,接收推理数据接口.
规范文件章节: N.3.1
:return:
"""
global rsServer, modelStatus
if modelStatus == 0: # 模型更新中,不可用
return jsonify(code=500)
try:
get_Data = request.get_data()
# # 判断传入的json数据是否为空
if get_Data is None or get_Data == "":
logger.warn(f'国家电网分析主机,分析缺陷,接收推理数据接口->收到非法数据')
return jsonify(code=400)
# 传入的参数为bytes类型,需要转化成json
get_Data = json.loads(get_Data)
data = EasyDict(get_Data)
logger.info(f'====>>>Request国家电网分析主机,分析缺陷,接收推理数据接口->接收到的数据:{data}')
# 数据合法性验证
isDataValidate, e = checkPicAnalysePackage(data)
if not isDataValidate:
logger.warn(f'国家电网分析主机,分析缺陷,接收推理数据接口->缺少必要数据:{e}')
return jsonify(code=400)
rsServer.putRS(("n311", data))
return jsonify(code=200)
except Exception as e:
logger.error(f'错误:{e}')
return jsonify(code=500)
@app.route(powerStationDefectConfig.requestAlgorithmUpdateURL, methods=["POST"])
def algorithmUpdate():
"""
三方算法模型更新请求。
规范章节:N.3.3
:return:
"""
global rsServer
try:
get_Data = request.get_data()
# # 判断传入的json数据是否为空
if get_Data is None or get_Data == "":
logger.warn(f'国家电网分析主机,分析缺陷,三方算法模型更新请求接口->收到非法数据')
return jsonify(code=400)
# 传入的参数为bytes类型,需要转化成json
get_Data = json.loads(get_Data)
data = EasyDict(get_Data)
logger.info(f'====>>>Request国家电网分析主机,分析缺陷,三方算法模型更新请求接口->接收到的数据:{data}')
# 数据合法性验证
isDataValidate, e = checkModelUpdatePackage(data)
if not isDataValidate:
logger.warn(f'国家电网分析主机,分析缺陷,三方算法模型更新请求接口->缺少必要数据:{e}')
return jsonify(code=400)
rsServer.putRS(("n331", data))
return jsonify(code=200)
except Exception as e:
logger.error(f'三方算法模型更新请求错误:{e}')
return jsonify(code=500)
# 更新算法模型
def downloadWeight(algorithmPath):
"""
下载模型权重,
权重命名规则:
1. 只有1个后缀,则认为是未加密权重,如:
exam.onnx,exam2.pt,examp.engine
2.如果有2个后缀,则认为是加密后缀,如
*.onnx.aes,*.pt.aes
3.如果有多个后缀,则认为不合法,下载失败
:param algorithmPath:根据规范此路径是ftps路径,ftp://username:password@url
:return: True/False:成功与否,weightName:权重文件名,加密后缀去除,可直接保存和加载,isEncrypt:是否加密的模型
"""
# 需要确定path格式再实现
logger.info(f' 模型下载地址:{algorithmPath}')
rs = urlparse(algorithmPath)
scheme = rs.scheme
userName = rs.username
password = rs.password
host = rs.hostname
port = rs.port
path = rs.path
modelPath, modelFile = os.path.split(path)
isEncrypt = True
d = modelFile.split(".")
if len(d) == 3: # *.onx.aes
modelName, modelType, x = d
elif len(d) == 2:
modelName, modelType = d
isEncrypt = False
logger.info(f' 模型加密:{isEncrypt}')
newModelFilePath = os.path.join(os.getcwd(), "weights", modelFile) # 存放算法模型文件
# saveModelDir = "/opt/project/2023/PowerTD/PowerStationDefect/3_Inference/weights/mask.txt"
logger.info(f' 模型存放目录:{newModelFilePath}')
try:
logger.info(f' 下载路径信息:host:{host},port:{port},user:{userName},password:{password}')
logger.info(f' 模型路径:{modelPath},模型文件:{modelFile},模型类型:{modelType}')
# ftp = FTP_TLS(host, userName,password) #注意规范中描述的使用ftps路径,bug:提示需要登录
ftp = FTP() # 注意规范中描述的使用ftps路径
ftp.connect(host=host, port=port)
ftp.login(user=userName, passwd=password)
ftp.cwd("/")
# print(f'{ftp.dir()}')
with open(newModelFilePath, 'wb') as f:
ftp.retrbinary('RETR ' + path, f.write)
logger.info(f' 模型文件下载完毕!')
return True, modelName + "." + modelType, isEncrypt
except Exception as e:
logger.error(f' 算法模型下载出错:{e}')
return False, "模型下载失败!", None
# 更新配置文件的权重配置且加载最新模型
def updateModel(modelFile, isEncrypt):
"""
更新配置文件的权重配置
:param modelFile: 权重文件名,权重文件需要是aes加密过的,且存在 weights/下
:return:
"""
global modelManager, model, modelStatus
modelStatus = 0 # 0更新中,1更新完毕,可正常工作
# 1.更新最新模型地址配置
updateModelWeightConfig(modelManager.modelConfigFile, "weights/" + modelFile)
updateConfigKeyData(modelManager.modelConfigFile, key="isEncrypt", value=isEncrypt)
# 2.更新模型
modelManager.reset()
model = modelManager.model
modelStatus = 1
return True, ""
# 三方图像推理请求,报文合法性检查
def checkPicAnalysePackage(picAnalysisData):
"""
检查数据格式是否正确
:param picAnalysisData: 三方请求的推理数据包,EasyDict类型
:return:
"""
if not hasattr(picAnalysisData, "requestHostIp"):
return False, "不存在requestHostIp属性"
if not hasattr(picAnalysisData, "requestHostPort"):
return False, "不存在requestHostPort属性"
if not hasattr(picAnalysisData, "requestId"):
return False, "不存在requestId属性"
if not hasattr(picAnalysisData, "objectList"):
return False, "不存在objectList属性"
if not isinstance(picAnalysisData.objectList, list):
return False, "objectList不是列表"
if len(picAnalysisData.objectList) == 0:
return False, "objectList为空"
for obj in picAnalysisData.objectList:
if not hasattr(obj, "objectId"):
return False, "不存在objectId属性"
if not hasattr(obj, "typeList"):
return False, "不存在typeList属性"
if not hasattr(obj, "imageNormalUrlPath"):
return False, "不存在imageNormalUrlPath属性"
if not hasattr(obj, "imageUrlList"):
return False, "不存在imageUrlList属性"
if not isinstance(obj.imageUrlList, list):
return False, "imageUrlList不是列表"
return True, ""
# 三方请求模型更新报文检查
def checkModelUpdatePackage(updateModelData):
"""
检查数据格式是否正确
:param updateModelData:模型更新请求
:return:
"""
if not hasattr(updateModelData, "requestHostIp"):
return False, "不存在requestHostIp属性"
if not hasattr(updateModelData, "requestHostPort"):
return False, "不存在requestHostPort属性"
if not hasattr(updateModelData, "requestId"):
return False, "不存在requestId属性"
if not hasattr(updateModelData, "algorithmPath"):
return False, "不存在algorithmPath属性"
return True, ""
# 时间戳
def getDateTimeS(ct):
local_time = time.localtime(ct)
data_head = time.strftime("%Y-%m-%d %H:%M:%S", local_time)
stamp = data_head
return stamp
def getDateTimeUUID(ct):
local_time = time.localtime(ct)
data_head = time.strftime("%Y%m%d%H%M%S", local_time)
stamp = data_head
return stamp
def inferenceHTTP():
app.run(powerStationDefectConfig.httpLocalIP, powerStationDefectConfig.httpLocalPort)
#
# server = pywsgi.WSGIServer(('0.0.0.0', 20011), app)
# server.serve_forever()
该处使用的url网络请求的数据。
提高变电站缺陷检测的准确率和召回率
为了提高缺陷检测的检测的准确率和召回率,您需要高质量的大型电站缺陷数据集,才能提供算法效率。变电站缺陷大型数据集联系方式为aricojf@163.com