first commit
This commit is contained in:
1 parent
ae53e9eac5
commit
91cbd01126
513 files changed
+55618
-2196
No files matched your search
Generated
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$" />
|
||||||
<orderEntry type="jdk" jdkName="Python 3.6 (py36pt18)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.6 (py36pt15)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PyDocumentationSettings">
|
<component name="PyDocumentationSettings">
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (py36pt18)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (py36pt15)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
// 使用 IntelliSense 了解相关属性。
|
||||||
|
// 悬停以查看现有属性的描述。
|
||||||
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python: Remote Attach",
|
||||||
|
"type": "python",
|
||||||
|
"request": "attach",
|
||||||
|
"connect": {
|
||||||
|
"host": "172.16.0.254",
|
||||||
|
"port": 22
|
||||||
|
},
|
||||||
|
"pathMappings": [
|
||||||
|
{
|
||||||
|
"localRoot": "${workspaceFolder}",
|
||||||
|
"remoteRoot": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"justMyCode": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.4 MiB |
File renamed without changes.
Executable
+50
@@ -0,0 +1,50 @@
|
|||||||
|
import os
|
||||||
|
from PIL import Image
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
#from scipy.misc import imread,imresize
|
||||||
|
import cv2,argparse
|
||||||
|
parser1 = argparse.ArgumentParser(description="CUI")
|
||||||
|
parser1.add_argument("root", default='/data1/Hatcher/model/project/data_MPKFNet/rawdata/竹林嘴-3月3日候鸟视频.mp4', type=str,
|
||||||
|
help='root.')
|
||||||
|
parser1.add_argument("imgsize", default='640', type=str,
|
||||||
|
help='imgsize')
|
||||||
|
args = parser1.parse_args()
|
||||||
|
filepath = args.root
|
||||||
|
imageSize = int(args.imgsize)
|
||||||
|
pathDir = os.listdir(filepath)
|
||||||
|
|
||||||
|
R_channel = 0
|
||||||
|
G_channel = 0
|
||||||
|
B_channel = 0
|
||||||
|
for idx in range(len(pathDir)):
|
||||||
|
filename = pathDir[idx]
|
||||||
|
print(filename)
|
||||||
|
img = cv2.imread(os.path.join(filepath, filename))
|
||||||
|
img = cv2.resize(img,(imageSize,imageSize))/ 255.0
|
||||||
|
R_channel = R_channel + np.sum(img[:, :, 0])
|
||||||
|
G_channel = G_channel + np.sum(img[:, :, 1])
|
||||||
|
B_channel = B_channel + np.sum(img[:, :, 2])
|
||||||
|
|
||||||
|
num = len(pathDir) * 512 * 512 # 这里(512,512)是每幅图片的大小,所有图片尺寸都一样
|
||||||
|
R_mean = R_channel / num
|
||||||
|
G_mean = G_channel / num
|
||||||
|
B_mean = B_channel / num
|
||||||
|
|
||||||
|
R_channel = 0
|
||||||
|
G_channel = 0
|
||||||
|
B_channel = 0
|
||||||
|
for idx in range(len(pathDir)):
|
||||||
|
filename = pathDir[idx]
|
||||||
|
print(filename)
|
||||||
|
img = cv2.imread(os.path.join(filepath, filename))
|
||||||
|
img = cv2.resize(img, (imageSize, imageSize)) / 255.0
|
||||||
|
R_channel = R_channel + np.sum((img[:, :, 0] - R_mean) ** 2)
|
||||||
|
G_channel = G_channel + np.sum((img[:, :, 1] - G_mean) ** 2)
|
||||||
|
B_channel = B_channel + np.sum((img[:, :, 2] - B_mean) ** 2)
|
||||||
|
|
||||||
|
R_var = np.sqrt(R_channel / num)
|
||||||
|
G_var = np.sqrt(G_channel / num)
|
||||||
|
B_var = np.sqrt(B_channel / num)
|
||||||
|
print("R_mean is %f, G_mean is %f, B_mean is %f" % (R_mean, G_mean, B_mean))
|
||||||
|
print("R_var is %f, G_var is %f, B_var is %f" % (R_var, G_var, B_var))
|
||||||
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
+1
-1
@@ -44,7 +44,7 @@ from Dcl.models.LoadModel import MainModel
|
|||||||
from Dcl.DclConfig import LoadConfig, load_data_transformers
|
from Dcl.DclConfig import LoadConfig, load_data_transformers
|
||||||
from Dcl.util.test_tool import set_text, save_multi_img, cls_base_acc
|
from Dcl.util.test_tool import set_text, save_multi_img, cls_base_acc
|
||||||
|
|
||||||
os.environ['CUDA_VISIBLE_DEVICES'] = '6'
|
#os.environ['CUDA_VISIBLE_DEVICES'] = '6'
|
||||||
devices = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
devices = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
||||||
|
|
||||||
class DclInference:
|
class DclInference:
|
||||||
|
|||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import shutil
|
||||||
|
from tqdm import tqdm
|
||||||
|
anno_pathRoot = "/data/DataManagement/Project/CAS_ProjectBird/CDB_ProjectBird/Annotations_Original/"
|
||||||
|
image_pathRoot = anno_pathRoot.replace("/Annotations_Original/","/JPEGImages_Original/")
|
||||||
|
w = 1024
|
||||||
|
h = 768
|
||||||
|
|
||||||
|
def DetResizeForTrain(anno_pathRoot,image_pathRoot,w,h):
|
||||||
|
annoResultPath = anno_pathRoot.replace("/Annotations_Original/","/Annotations/")
|
||||||
|
annoJPEGPath = anno_pathRoot.replace("/JPEGImages_Original/", "/JPEGImages/")
|
||||||
|
for anno in tqdm(os.listdir(anno_pathRoot)):
|
||||||
|
anno = GEN_Annotations(anno.replace(".xml", ".jpg"))
|
||||||
|
dom = xml.dom.minidom.parse(os.path.join(anno_pathRoot, xmls))
|
||||||
|
size = dom.getElementsByTagName("size")
|
||||||
|
# print(size[0].getElementsByTagName('width')[0].childNodes[0].data)
|
||||||
|
width = size[0].getElementsByTagName('width')[0].childNodes[0].data
|
||||||
|
height = size[0].getElementsByTagName('height')[0].childNodes[0].data
|
||||||
|
depth = size[0].getElementsByTagName('depth')[0].childNodes[0].data
|
||||||
|
anno.set_size(int(w), int(h), int(depth))
|
||||||
|
objects = dom.getElementsByTagName("object")
|
||||||
|
for object in objects:
|
||||||
|
bndbox = object.getElementsByTagName('bndbox')[0]
|
||||||
|
xmin = bndbox.getElementsByTagName('xmin')[0]
|
||||||
|
ymin = bndbox.getElementsByTagName('ymin')[0]
|
||||||
|
xmax = bndbox.getElementsByTagName('xmax')[0]
|
||||||
|
ymax = bndbox.getElementsByTagName('ymax')[0]
|
||||||
|
|
||||||
|
xmin_data = int(xmin.childNodes[0].data)*(w/width)
|
||||||
|
ymin_data = int(ymin.childNodes[0].data)*(h/height)
|
||||||
|
xmax_data = int(xmax.childNodes[0].data)*(w/width)
|
||||||
|
ymax_data = int(ymax.childNodes[0].data)*(h/height)
|
||||||
|
label_name = object.getElementsByTagName('name')[0].childNodes[0].data
|
||||||
|
anno.add_pic_attr(label=label_name, xmin=xmin_data, xmax=xmax_data, ymin=ymin_data, ymax=ymax_data)
|
||||||
|
anno.savefile(os.path.join(annoResultPath, xmls))
|
||||||
|
image = cv2.imread
|
||||||
Executable
+273
@@ -0,0 +1,273 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
import cv2,os,torch,argparse
|
||||||
|
import queue
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from System_I import Check_start_fordataselect,Density,Detection_Y5,Fine_grained,Fine_grained_T,shishi,save_sigle_image,RealDeal
|
||||||
|
from common import global_para as gp
|
||||||
|
r_db = None
|
||||||
|
import torch.multiprocessing as multiprocessing
|
||||||
|
import sort.sort as SORT
|
||||||
|
from imatovideo import images_to_video
|
||||||
|
from multiprocessing import Array
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DetFineVid():
|
||||||
|
def __init__(self, VideoPath,SavePath):
|
||||||
|
super(DetFineVid,self).__init__()
|
||||||
|
# name
|
||||||
|
self.VideoPath = VideoPath
|
||||||
|
self.VideoResultSavePathroot = "/data1/Hatcher/model/project/data_MPKFNet/result_vedio/"
|
||||||
|
self.VideoName = os.path.basename(self.VideoPath).split(".")[0]
|
||||||
|
self.VideoResultSavePath = os.path.join(self.VideoResultSavePathroot,"{}.avi".format(self.VideoName))
|
||||||
|
self.SavePath = os.path.join(SavePath,self.VideoName)
|
||||||
|
if not os.path.exists(self.SavePath):
|
||||||
|
os.makedirs(self.SavePath)
|
||||||
|
|
||||||
|
self.root = "/home/wanghc/output_jiankong"
|
||||||
|
self.DensityClass = Density(gp.devices, self.root)
|
||||||
|
self.DetectionClass = Detection_Y5(gp.devices, self.root)
|
||||||
|
self.Fine_grianedClass = Fine_grained_T(gp.devices)
|
||||||
|
self.FineRestartFlag = True
|
||||||
|
self.numrestart = 0
|
||||||
|
|
||||||
|
def conVideo(self):
|
||||||
|
images_to_video( self.SavePath,self.VideoResultSavePath)
|
||||||
|
def StartRec(self):
|
||||||
|
|
||||||
|
|
||||||
|
t1 = time.time()
|
||||||
|
cap = cv2.VideoCapture(self.VideoPath)
|
||||||
|
t2 = time.time()
|
||||||
|
print("capture is ",(t2-t1)*1000)
|
||||||
|
num_detection = 0
|
||||||
|
num_cuntupian=0
|
||||||
|
SortRestart = SORT.Sort(max_age=gp.max_age,
|
||||||
|
min_hits=gp.min_hits,
|
||||||
|
iou_threshold=gp.sortiou_threshold)
|
||||||
|
realtimedeal = RealDeal("check_start",self.DetectionClass,self.Fine_grianedClass,self.DensityClass)
|
||||||
|
if cap.isOpened():
|
||||||
|
res, frame = cap.read()
|
||||||
|
if res:
|
||||||
|
shapex = frame.shape[1]
|
||||||
|
shapey = frame.shape[0]
|
||||||
|
gp.radioShowX = shapex / gp.radioShowX
|
||||||
|
gp.radioShowY = shapey / gp.radioShowY
|
||||||
|
gp.radioShowtext = gp.radioShowX
|
||||||
|
if frame.shape[1] == 3840:
|
||||||
|
gp.fontScale = 3
|
||||||
|
elif frame.shape[1] == 2560:
|
||||||
|
gp.fontScale = 1
|
||||||
|
else:
|
||||||
|
gp.fontScale = 1
|
||||||
|
for i in range(20):
|
||||||
|
res, frame = cap.read()
|
||||||
|
print("skip{}".format(i))
|
||||||
|
|
||||||
|
num_image = 0
|
||||||
|
while True:
|
||||||
|
if cap.isOpened():
|
||||||
|
res, frame = cap.read()
|
||||||
|
if res:
|
||||||
|
closed = False
|
||||||
|
failCnt = 0
|
||||||
|
num_image += 1
|
||||||
|
data_today = 8
|
||||||
|
print(num_detection)
|
||||||
|
# if num_detection <12974:
|
||||||
|
# a=0
|
||||||
|
# num_detection+=1
|
||||||
|
# continue
|
||||||
|
#cv2.imwrite("./test.jpg",frame)
|
||||||
|
if (int(data_today) > 1) & (int(data_today) < 20):
|
||||||
|
if (0xFF == ord('q')):
|
||||||
|
print("restart")
|
||||||
|
self.FineRestartFlag = True
|
||||||
|
self.numrestart = 0
|
||||||
|
SortRestart = SORT.Sort(max_age=gp.max_age,
|
||||||
|
min_hits=gp.min_hits,
|
||||||
|
iou_threshold=gp.sortiou_threshold)
|
||||||
|
if self.FineRestartFlag :
|
||||||
|
realtimedeal.realtimerestart(frame,num_image=num_image,num_detection=num_detection,SortRestart = SortRestart)
|
||||||
|
self.numrestart += 1
|
||||||
|
if self.numrestart >= 5:
|
||||||
|
self.numrestart = 0
|
||||||
|
self.FineRestartFlag = False
|
||||||
|
FirstDetFineResult = realtimedeal.getrestartresult(gp.IndexMax)
|
||||||
|
print("µÚÒ»´Îʶ±ðÄ¿±êÊýÁ¿ ",len(FirstDetFineResult))
|
||||||
|
gp.SortLabel_Index = FirstDetFineResult
|
||||||
|
# SortRealTime = SORT.Sort(max_age=gp.max_age,
|
||||||
|
# min_hits=gp.min_hits,
|
||||||
|
# iou_threshold=gp.sortiou_threshold)
|
||||||
|
|
||||||
|
else:
|
||||||
|
tm1 = time.time()
|
||||||
|
#frame = cv2.imread("./test.jpg")
|
||||||
|
img_show,num_image = realtimedeal.realtime(frame,num_image=num_image,num_detection=num_detection,SortRestart = SortRestart)
|
||||||
|
tm2 = time.time()
|
||||||
|
if gp.DebugPrint_flag:
|
||||||
|
print("main1 time is ", (tm2 - tm1) * 1000)
|
||||||
|
# cv2.imshow("AI-ECO", img_show)
|
||||||
|
# #cv2.imwrite("../data_MPKFNet/RTSP3/" + "{}.jpg".format(time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))),img_show)
|
||||||
|
# if cv2.waitKey(1) & 0xFF == ord('r'):
|
||||||
|
# break
|
||||||
|
save_path =os.path.join(self.SavePath,"{}.jpg".format(str(num_detection).zfill(6)))
|
||||||
|
cv2.imwrite(save_path,img_show)
|
||||||
|
tm3 = time.time()
|
||||||
|
if gp.DebugPrint_flag:
|
||||||
|
print("main2 time is ", (tm3 - tm2) * 1000)
|
||||||
|
else:
|
||||||
|
print("time is out")
|
||||||
|
if (num_detection >1000000) :
|
||||||
|
# save_image = save_sigle_image(frame)
|
||||||
|
# save_image.start()
|
||||||
|
# num_detection=0
|
||||||
|
num_detection = 0
|
||||||
|
num_detection+=1
|
||||||
|
num_cuntupian+=1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def commanddeal():
|
||||||
|
SavePath = "/data1/Hatcher/model/project/data_MPKFNet/result/"
|
||||||
|
print("starting ",gp.VideoPath)
|
||||||
|
DFS = DetFineVid(gp.VideoPath,SavePath)
|
||||||
|
DFS.StartRec()
|
||||||
|
DFS.conVideo()
|
||||||
|
def filedeal(rootPath): #deal all file in a folder
|
||||||
|
SavePath = "/data3/DEMO/images/"
|
||||||
|
i = 0
|
||||||
|
for video_path in os.listdir(rootPath):
|
||||||
|
if i == 1:
|
||||||
|
print("111")
|
||||||
|
# print("starting deal {}".format(str(video_path)))
|
||||||
|
# VideoPath = os.path.join(rootPath,video_path)
|
||||||
|
# DFS = DetFineVid(VideoPath,SavePath)
|
||||||
|
# DFS.StartRec()
|
||||||
|
# DFS.conVideo()
|
||||||
|
P = multiprocessing.Process(target=func,args=(video_path,SavePath,rootPath))
|
||||||
|
P.start()
|
||||||
|
P.join()
|
||||||
|
#globalInit()
|
||||||
|
i += 1
|
||||||
|
def func(video_path,SavePath,rootPath):
|
||||||
|
print("starting deal {}".format(str(video_path)))
|
||||||
|
VideoPath = os.path.join(rootPath, video_path)
|
||||||
|
DFS = DetFineVid(VideoPath, SavePath)
|
||||||
|
DFS.StartRec()
|
||||||
|
DFS.conVideo()
|
||||||
|
def globalInit(): #deal all file in a folder
|
||||||
|
|
||||||
|
parser1 = argparse.ArgumentParser(description="CUI")
|
||||||
|
parser1.add_argument("--video_num", default='/data1/Hatcher/model/project/data_MPKFNet/rawdata/PYH-DEMO.mp4',
|
||||||
|
type=str,
|
||||||
|
help='Video name.')
|
||||||
|
parser1.add_argument("--GPU", default='6', type=str,
|
||||||
|
help='GPU.')
|
||||||
|
parser1.add_argument("--thresh", default=0.4, type=float,
|
||||||
|
help='GPU.')
|
||||||
|
parser1.add_argument("--red", default=0, type=int,
|
||||||
|
help='red.')
|
||||||
|
parser1.add_argument("--add", default=0, type=str,
|
||||||
|
help='add.')
|
||||||
|
|
||||||
|
args = parser1.parse_args()
|
||||||
|
# print(args)
|
||||||
|
gp.root = "/home/gyd/output1"
|
||||||
|
# os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||||
|
os.environ['CUDA_VISIBLE_DEVICES'] = args.GPU
|
||||||
|
gp.devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||||
|
gp.camera_address = args.add
|
||||||
|
gp.VideoPath = args.video_num
|
||||||
|
gp.red_flag = args.red
|
||||||
|
gp.detect_thresh = args.thresh
|
||||||
|
|
||||||
|
# fine_thresh2 = torch.tensor(fine_thresh2).cuda()
|
||||||
|
gp.birdIndex = 99999
|
||||||
|
# birdIndex = torch.tensor(birdIndex).cuda()
|
||||||
|
gp.is_ex_bird = False # Ä¿±ê¼ì²â±ê־λ£¬Ã¿¸öÒ»Ãë¼ì²âÒ»´ÎÄ¿±ê¼ì²â£¬Èç¹ûÓÐÄñ£¬ÔòÖÃΪTrue£¬ÎªTrueµÄʱºò˵Ã÷£¬ ´¦Àí30sµÄÏ̻߳¹ÔÚ¼ÌÐø£¬Ö±µ½±ê־λ±äΪÁËFalseÔÙ¿ªÆôеÄÏß³Ì
|
||||||
|
gp.Density_thread_flag = False # ÃܶȹÀ¼ÆÏ̱߳ê־λ£¬ÎªTrue˵Ã÷¿ªÆôÁËÃܶȹÀ¼ÆỊ̈߳¬´¦ÀíÍê750֡Ϊֹ£¬±ê־λ±êΪFalse£¬
|
||||||
|
gp.Detection_thread_flag = False # Ä¿±ê¼ì²âºÍϸÁ£¶Èʶ±ðÏ̱߳ê־λ£¬ÎªTrue˵Ã÷¿ªÆôÁËÃܶȹÀ¼ÆỊ̈߳¬´¦ÀíÍê750֡Ϊֹ£¬±ê־λ±êΪFalse£¬
|
||||||
|
gp.Vedio_thread_flag = False # ´æ´¢ÊÓÆµÏ̱߳ê־룬´æ´¢30s
|
||||||
|
gp.SaveImage_thread_flag = False # ±£´æÔͼÏ̱߳ê־룬´æ´¢30ÕÅÔͼ
|
||||||
|
gp.ALL_thread_flag = False # ËùÓÐÏ̵߳ıê־룬µ¥¶À¿ªÉèÒ»¸öỊ̈߳¬¼à¿ØËùÓÐÏß³ÌÊÇ·ñ¶¼½áÊø£¬Èç¹û¶¼½áÊø£¬ÔòÖÃΪFalse£¬ºóÐøÌí¼ÓÏ̹߳ÜÀí£¬¿ÉÒÔ·ÅÔÚÕâÀïÃæ
|
||||||
|
|
||||||
|
gp.ShowAllNumFlag = False
|
||||||
|
|
||||||
|
gp.DebugPrint_flag = False # ´òÓ¡±ê־λ
|
||||||
|
|
||||||
|
gp.num_count_old = 0
|
||||||
|
gp.boxes_fine = None
|
||||||
|
gp.labels_fine = None
|
||||||
|
|
||||||
|
gp.showThresh = 25
|
||||||
|
gp.destory_image = False
|
||||||
|
gp.Camera_device = args.video_num # ÉãÏñÍ·±àºÅ£¬0001
|
||||||
|
gp.image_time = None # µ±Ç°´¦ÀíµÄ30ÊÓÆµµÄ±àºÅ
|
||||||
|
gp.ImgDealQueue = [] # 750Ö¡ÊÓÆµ£¬»º´æ¿Õ¼ä
|
||||||
|
gp.img_show = None
|
||||||
|
|
||||||
|
# event = threading.Event()
|
||||||
|
gp.eventDensity = threading.Event()
|
||||||
|
gp.eventDetection = threading.Event()
|
||||||
|
gp.eventSaveimage = threading.Event()
|
||||||
|
gp.max_age = 2
|
||||||
|
gp.min_hits = 3
|
||||||
|
gp.sortiou_threshold = 0.3
|
||||||
|
gp.Finerestartboxes = []
|
||||||
|
gp.IndexMax = 0
|
||||||
|
gp.radioShowX = 2560.0
|
||||||
|
gp.radioShowY = 1440.0
|
||||||
|
gp.radioShowtext = gp.radioShowX
|
||||||
|
gp.SortLabel_Index = None # ±êÇ©ºÍË÷Òý¶ÔÓ¦¹ØÏµ[[4,1],[3,2]]
|
||||||
|
gp.SortLabel_Indexpre = [] # ±êÇ©ºÍË÷ÒýµÄ¶ÔÓ¦¹ØÏµ£¬ÓÃÔÚ×·×Ù¹ý³ÌÖÐгöÏÖµÄÄ¿±ê£¬[[label,Index,Count]]
|
||||||
|
# Detection2 = Detection(devices,root)
|
||||||
|
gp.FineCountThresh = 5
|
||||||
|
|
||||||
|
gp.img_show = None
|
||||||
|
|
||||||
|
gp.Fine_thresh = 0.4
|
||||||
|
|
||||||
|
# DetWeight = "/data1/Hatcher/model/Detection/yolov5/runs/train/exp21/weights/best.pt"
|
||||||
|
# FineWeight = "/data/Hatcher/data/weigth/areaproject/resnet50-PYH-3-20.7/_42522_PYH-3/best.pth"
|
||||||
|
gp.DetWeight = "/data1/Hatcher/model/Detection/yolov5/runs/train/KunMingCuiHu-Fish2/weights/best.pt"
|
||||||
|
gp.FineWeight = "/data/Hatcher/data/weigth/areaproject/resnet50-DianChiFish-3-3-0.7/_4289_DianChiFish-4/best.pth"
|
||||||
|
# DetWeight = "/data1/Hatcher/model/Detection/yolov5/runs/train/BeijingCuiHu-Bird26/weights/best.pt"
|
||||||
|
# FineWeight = "/data/Hatcher/data/weigth/areaproject/resnet50-cui90_1model0.7/_42111_CUI-94-1/best.pth"
|
||||||
|
# BHG_chineseName = ["kong", '白鹤', '白头鹤','白枕鹤']#["kong","鲫鱼","麦穗鱼","鳑鲏"]
|
||||||
|
gp.BHG_chineseName = ["kong", "鲫鱼", "麦穗鱼", "鳑鲏"]
|
||||||
|
|
||||||
|
# BHG_chineseName = ['Kong', '白额雁', '白骨顶', '苍鹭', '苍鹭', '白秋沙鸭', '白腰草鹬', '斑头雁', '斑嘴鸭', '苍鹭', '赤膀鸭', '赤颈鸭', '苍鹭', '大麻鳽',
|
||||||
|
# '豆雁', '反嘴鹬', '凤头鸊鷉', '凤头麦鸡', '凤头潜鸭', '黑鹳', '黑水鸡', '红嘴鸥', '鸿雁', '灰鹤', '灰雁', '绿翅鸭', '绿头鸭', '琵嘴鸭',
|
||||||
|
# '普通鸬鹚', '普通秋沙鸭', '翘鼻麻鸭', '鹊鸭', '扇尾沙锥', '小白额雁', '小天鹅', '针尾鸭', '苍鹭', '暗绿绣眼鸟', '八哥', '白腹鹞', '白鹡鸰',
|
||||||
|
# '白眼潜鸭', '草鹭', '池鹭', '戴胜', '黑翅长脚鹬', '黑鸢', '红隼', '花脸鸭', '环颈雉', '灰头绿啄木鸟', '灰头麦鸡', '矶鹬', '栗苇鳽',
|
||||||
|
# '罗纹鸭', '牛背鹭', '普通翠鸟', '雀鹰', '喜鹊', '苍鹭', '鸳鸯', '棕背伯劳', '红胸秋沙鸭', '普通燕鸥', '小白额燕鸥', '丘鹬', '凤头蜂鹰',
|
||||||
|
# '大天鹅', '大鸨', '小田鸡', '山斑鸠', '帆背潜鸭', '日本松雀鹰', '普通鵟', '松雀鹰', '毛脚鵟', '灰斑鸠', '白头鹞', '白尾鹞', '白眉鸭',
|
||||||
|
# '白鹈鹕', '红头潜鸭', '苍鹰', '蒙古银鸥', '西方秧鸡', '赤嘴潜鸭', '阔嘴鹬', '雕鸮', '鹌鹑', '鹗', '黄苇鳽', '黑颈鹤', '小䴙䴘', '赤麻鸭']
|
||||||
|
# BHG_chineseName =["kong","绿头鸭","斑嘴鸭","灰雁","鸿雁","kong","小鷿鷈"]
|
||||||
|
|
||||||
|
gp.FineClassNum = 4
|
||||||
|
gp.color_show = (255, 255, 255)
|
||||||
|
gp.color_box = (255, 0, 0)
|
||||||
|
gp.NoneLabel = "鱼" # "鹤类"
|
||||||
|
gp.fine_thresh1 = 6000
|
||||||
|
gp.fine_thresh2 = 10000
|
||||||
|
gp.fontScale = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
torch.zero_(gp.Species)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
multiprocessing.set_start_method("spawn")
|
||||||
|
rootPath = "/data3/DEMO/video/PYH/"
|
||||||
|
filedeal(rootPath)
|
||||||
|
#commanddeal()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Executable
+176
@@ -0,0 +1,176 @@
|
|||||||
|
# -- coding: utf-8 --
|
||||||
|
# @Author : Calvin
|
||||||
|
# @Email : 934055521@qq.com
|
||||||
|
# @File : xld.py
|
||||||
|
# @use :
|
||||||
|
# @Software: PyCharm
|
||||||
|
# @Time : 27/5/2022 3:32 PM
|
||||||
|
"""
|
||||||
|
python3 FinegrainedIdentificationDatasetGenerate.py
|
||||||
|
-rp /data/DataManagement/Project/004_kunmingcuihu/0003_20220507_800/CAS_KMCuihuBird/CMB_KMCuihuBird
|
||||||
|
-blp /data/DataManagement/Project/004_kunmingcuihu/0003_20220507_800/CAS_KMCuihuBird/CMB_KMCuihuBird/birdList/KMCuihuBird.xlsx
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import argparse
|
||||||
|
import random
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
from tqdm import tqdm
|
||||||
|
BHG_chineseName = ['bird', 'Anser_albifrons', 'Fulica_atra', 'Egretta_garzetta', 'Platalea_leucorodia', 'Mergellus_albellus', 'Tringa_ochropus', 'Anser_indicus', 'Anas_zonorhyncha', 'Ardea_cinerea', 'Mareca_strepera', 'Mareca_penelope', 'Ardea_alba', 'Botaurus_stellaris', 'Anser_fabalis', 'Recurvirostra_avosetta', 'Podiceps_cristatus', 'Vanellus_vanellus', 'Aythya_fuligula', 'Ciconia_nigra', 'Gallinula_chloropus', 'Chroicocephalus_ridibundus', 'Anser_cygnoides', 'Grus_grus', 'Anser_anser', 'Anas_crecca', 'Anas_platyrhynchos', 'Spatula_clypeata', 'Phalacrocorax_carbo', 'Mergus_merganser', 'Tadorna_tadorna', 'Bucephala_clangula', 'Gallinago_gallinago', 'Anser_erythropus', 'Cygnus_columbianus', 'Anas_acuta', 'Ardea_intermedia', 'Zosterops_simplex', 'Acridotheres_cristatellus', 'Circus_spilonotus', 'Motacilla_alba', 'Aythya_nyroca', 'Ardea_purpurea', 'Ardeola_bacchus', 'Upupa_epops', 'Himantopus_himantopus', 'Milvus_migrans', 'Falco_tinnunculus', 'Sibirionetta_formosa', 'Phasianus_colchicus', 'Picus_canus', 'Vanellus_cinereus', 'Actitis_hypoleucos', 'Ixobrychus_cinnamomeus', 'Mareca_falcata', 'Bubulcus_coromandus', 'Alcedo_atthis', 'Accipiter_nisus', 'Pica_serica', 'Nycticorax_nycticorax', 'Aix_galericulata', 'Lanius_schach', 'Mergus_serrator', 'Sterna_hirundo', 'Sternula_antillarum', 'Scolopax_rusticola', 'Pernis_ptilorhynchus', 'Cygnus_cygnus', 'Otis_tarda', 'Zapornia_pusilla', 'Streptopelia_orientalis', 'Aythya_valisineria', 'Accipiter_gularis', 'Buteo_japonicus', 'Accipiter_virgatus', 'Buteo_lagopus', 'Streptopelia_decaocto', 'Circus_aeruginosus', 'Circus_cyaneus', 'Spatula_querquedula', 'Pelecanus_onocrotalus', 'Aythya_ferina', 'Accipiter_gentilis', 'Larus_cachinnans', 'Rallus_aquaticus', 'Netta_rufina', 'Calidris_falcinellus', 'Bubo_bubo', 'Coturnix_japonica', 'Pandion_haliaetus', 'Ixobrychus_sinensis', 'Grus_nigricollis', 'Tachybaptus_ruficollis', 'Tadorna_ferruginea', 'Leucogeranus_leucogeranus', 'Grus_monacha', 'Antigone_vipio', 'Cyanopica_cyanus', 'Passer_montanus', 'Hirundo_rustica']
|
||||||
|
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
Number_last = 0
|
||||||
|
LabelSpecies = []
|
||||||
|
DataNOlineFeed = []
|
||||||
|
WrongImages = []
|
||||||
|
DataSpeciesClassier = dict()
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
|
||||||
|
def parse():
|
||||||
|
arg = argparse.ArgumentParser()
|
||||||
|
arg.add_argument('--root_path', '-rp', default='/data/DataManagement/Project/CAS_ProjectBird/CMB_ProjectBird0615/', type=str)
|
||||||
|
arg.add_argument('--division_ratio', '-dr', default='0.7', type=float)
|
||||||
|
arg.add_argument('--bird_list_path', '-blp', default='/data/DataManagement/Project/CAS_ProjectBird/CMB_ProjectBird0615/birdList/ProjectBirdList.xlsx')
|
||||||
|
args = arg.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse()
|
||||||
|
dataset_txt_path = os.path.join(args.root_path, 'DatasetTxt')
|
||||||
|
species_txt_path = f'{args.root_path}/DatasetTxt/Species.txt'
|
||||||
|
dataset_spectrum_path = f'{args.root_path}/OrginalData'
|
||||||
|
dataset_allspectrum_path = f'{args.root_path}/FinalData/data'
|
||||||
|
dataset_allspectrum_path_train = f'{args.root_path}/FinalData/train'
|
||||||
|
dataset_allspectrum_path_test = f'{args.root_path}/FinalData/test'
|
||||||
|
dataset_allspectrum_txt_path = f'{args.root_path}/FinalData/anno/all.txt'
|
||||||
|
if not os.path.exists(dataset_txt_path):
|
||||||
|
os.makedirs(dataset_txt_path)
|
||||||
|
# for data in tqdm(os.listdir(dataset_spectrum_path), ncols=80, ascii=True, desc="OrginalData images path"):
|
||||||
|
# # for data in os.listdir(dataset_spectrum_path):
|
||||||
|
# for image in os.listdir(os.path.join(dataset_spectrum_path, data)):
|
||||||
|
# try:
|
||||||
|
# im = cv2.imread(os.path.join(dataset_spectrum_path, data, image))
|
||||||
|
# except:
|
||||||
|
# WrongImages.append(os.path.join(dataset_spectrum_path, data, image))
|
||||||
|
# os.remove(os.path.join(dataset_spectrum_path, data, image))
|
||||||
|
# tqdm.write('Wrong images are {:}'.format(WrongImages))
|
||||||
|
bird_list = pd.read_excel(args.bird_list_path, usecols=['拉丁名'])
|
||||||
|
bird_list = np.array(bird_list.stack()).tolist()
|
||||||
|
for num, data in tqdm(enumerate(bird_list), ncols=80, ascii=True, desc="writing species.txt"):
|
||||||
|
# for num, data in enumerate(bird_list):
|
||||||
|
with open(species_txt_path, 'a') as f:
|
||||||
|
filename = data
|
||||||
|
file_final_name = data + ' ' + str(num)
|
||||||
|
f.write(file_final_name)
|
||||||
|
f.write('\n')
|
||||||
|
for speices in tqdm(os.listdir(dataset_spectrum_path), ncols=80, ascii=True, desc="writing species.txt"):
|
||||||
|
# for speices in os.listdir(dataset_spectrum_path):
|
||||||
|
for data in os.listdir(os.path.join(dataset_spectrum_path, speices)):
|
||||||
|
if data.split('___')[0] != speices:
|
||||||
|
os.rename(os.path.join(dataset_spectrum_path, speices, data), os.path.join(dataset_spectrum_path, speices, '{:}___{:}'.format(speices, data)))
|
||||||
|
tqdm.write('The pictures naming is done!')
|
||||||
|
|
||||||
|
with open(species_txt_path, 'r') as file:
|
||||||
|
Data = file.readlines()
|
||||||
|
for line in Data:
|
||||||
|
line = line.strip('\n')
|
||||||
|
species = line.split(' ')[0]
|
||||||
|
Number = line.split(' ')[1]
|
||||||
|
#Judge Number
|
||||||
|
if Number != Number_last:
|
||||||
|
DataSpeciesClassier[species] = Number
|
||||||
|
Number_last = Number
|
||||||
|
|
||||||
|
tqdm.write(str(DataSpeciesClassier))
|
||||||
|
tqdm.write('The species have been obtained')
|
||||||
|
|
||||||
|
# transfer single files to all files
|
||||||
|
if not os.path.exists(dataset_allspectrum_path):
|
||||||
|
os.makedirs(dataset_allspectrum_path)
|
||||||
|
if not os.path.exists(dataset_allspectrum_path_train):
|
||||||
|
os.makedirs(dataset_allspectrum_path_train)
|
||||||
|
if not os.path.exists(dataset_allspectrum_path_test):
|
||||||
|
os.makedirs(dataset_allspectrum_path_test)
|
||||||
|
for data in tqdm(os.listdir(dataset_spectrum_path), ncols=80, ascii=True, desc="transfer single files"):
|
||||||
|
# for data in os.listdir(dataset_spectrum_path):
|
||||||
|
for file in os.listdir(os.path.join(dataset_spectrum_path, data)):
|
||||||
|
shutil.copy(os.path.join(dataset_spectrum_path, data, file), dataset_allspectrum_path)
|
||||||
|
tqdm.write('Transfer single files to all files is done')
|
||||||
|
|
||||||
|
# generate the all.txt
|
||||||
|
if not os.path.exists('/'.join(dataset_allspectrum_txt_path.split('/')[:-1])):
|
||||||
|
os.makedirs('/'.join(dataset_allspectrum_txt_path.split('/')[:-1]))
|
||||||
|
for data in tqdm(os.listdir(dataset_allspectrum_path), ncols=80, ascii=True, desc="transfer single files"):
|
||||||
|
# for data in os.listdir(dataset_allspectrum_path):
|
||||||
|
with open(dataset_allspectrum_txt_path, 'a') as f:
|
||||||
|
file_new_name = data.split('___')[0]
|
||||||
|
# file_new_name = data
|
||||||
|
# file_new_name = '_'.join(file_new_name.split('_')[:-1])
|
||||||
|
num = DataSpeciesClassier[file_new_name]
|
||||||
|
file_final_name = data + ' ' + num
|
||||||
|
f.write(file_final_name)
|
||||||
|
f.write('\n')
|
||||||
|
tqdm.write('Generate the all.txt is done')
|
||||||
|
|
||||||
|
# Spilit dataset
|
||||||
|
DataPath = dataset_allspectrum_txt_path
|
||||||
|
# delete file_txt
|
||||||
|
if os.path.exists(DataPath.replace(os.path.basename(DataPath), 'train.txt')) :
|
||||||
|
os.remove(DataPath.replace(os.path.basename(DataPath), 'train.txt'))
|
||||||
|
if os.path.exists(DataPath.replace(os.path.basename(DataPath), 'test.txt')):
|
||||||
|
os.remove(DataPath.replace(os.path.basename(DataPath), 'test.txt'))
|
||||||
|
|
||||||
|
with open(DataPath, 'r')as f:
|
||||||
|
Data = f.readlines()
|
||||||
|
LabelSpecies = []
|
||||||
|
for RowData in Data:
|
||||||
|
RowData = RowData.strip()
|
||||||
|
# find picture-label
|
||||||
|
RowLabels = RowData.split(' ')[1]
|
||||||
|
DataNOlineFeed.append(RowData.split(' '))
|
||||||
|
if RowLabels not in LabelSpecies:
|
||||||
|
LabelSpecies.append(RowLabels)
|
||||||
|
print(LabelSpecies)
|
||||||
|
tqdm.write("classes num of Dataset is : {:}".format(len(LabelSpecies)))
|
||||||
|
for Labels in tqdm(LabelSpecies, ncols=80, ascii=True, desc="transfer single files"):
|
||||||
|
# for Labels in LabelSpecies:
|
||||||
|
Num = 0
|
||||||
|
TrainData = []
|
||||||
|
TestData = []
|
||||||
|
TotalData = []
|
||||||
|
for SpeciesData in DataNOlineFeed:
|
||||||
|
if Labels == SpeciesData[1]:
|
||||||
|
Num += 1
|
||||||
|
TotalData.append(SpeciesData)
|
||||||
|
|
||||||
|
TrainNum = int(Num * args.division_ratio)
|
||||||
|
TestNum = Num - TrainNum
|
||||||
|
random.shuffle(TotalData)
|
||||||
|
with open(DataPath.replace(os.path.basename(DataPath), 'train.txt'), 'a') as f:
|
||||||
|
for i in range(TrainNum):
|
||||||
|
f.write(' '.join(TotalData[i]) + '\n')
|
||||||
|
label_classname = "{}.{}".format(str(Labels).zfill(4),BHG_chineseName[int(Labels)])
|
||||||
|
soursepathTrain = os.path.join(dataset_spectrum_path,label_classname)
|
||||||
|
destpathTrain = os.path.join(dataset_allspectrum_path_train,label_classname)
|
||||||
|
if not os.path.exists(destpathTrain):
|
||||||
|
os.makedirs(destpathTrain)
|
||||||
|
image_name = TotalData[i][0]
|
||||||
|
shutil.copy(os.path.join(soursepathTrain,str(image_name)),os.path.join(destpathTrain,str(image_name)))
|
||||||
|
|
||||||
|
with open(DataPath.replace(os.path.basename(DataPath), 'test.txt'), 'a') as f:
|
||||||
|
for i in range(TrainNum, Num):
|
||||||
|
f.write(' '.join(TotalData[i]) + '\n')
|
||||||
|
label_classname = "{}.{}".format(str(Labels).zfill(4), BHG_chineseName[int(Labels)])
|
||||||
|
soursepathTest = os.path.join(dataset_spectrum_path, label_classname)
|
||||||
|
destpathTest = os.path.join(dataset_allspectrum_path_test, label_classname)
|
||||||
|
if not os.path.exists(destpathTest):
|
||||||
|
os.makedirs(destpathTest)
|
||||||
|
image_name = TotalData[i][0]
|
||||||
|
shutil.copy(os.path.join(soursepathTest, str(image_name)), os.path.join(destpathTest, str(image_name)))
|
||||||
|
|
||||||
|
|
||||||
Regular → Executable
+24
-14
@@ -1,30 +1,40 @@
|
|||||||
import os,argparse,torch
|
import os,argparse,torch
|
||||||
from System_I import Detection_Y5
|
from System_I import Detection_Y5,Fine_grained_T
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
#os.environ['CUDA_VISIBLE_DEVICES'] = "1"
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser1 = argparse.ArgumentParser(description="AILabel")
|
parser1 = argparse.ArgumentParser(description="AILabel")
|
||||||
parser1.add_argument("--RawPath", default='/data/DataManagement/Project/001_cuihu/0012_20220517_RTSP/imagesAILabel', type=str,
|
parser1.add_argument("--RawPath", default='/data/DataManagement/Project/006_huanghekou/0006_20220621/Raw_images/imagesAILabel', type=str,
|
||||||
help='RawPath.')
|
help='RawPath.')
|
||||||
parser1.add_argument("--GPU", default="1", type=str,
|
parser1.add_argument("--GPU", default="4", type=str,
|
||||||
help='GPU.')
|
help='GPU.')
|
||||||
parser1.add_argument("--thresh", default=0.4, type=float,
|
parser1.add_argument("--thresh", default=0.4, type=float,
|
||||||
help='thresh.')
|
help='thresh.')
|
||||||
parser1.add_argument("--Weight", default="/data/Hatcher/data/Detection_weigth/BJCHKMCH-Bird-1-4/weights/best.pt", type=str,
|
|
||||||
help='Weight.')
|
|
||||||
args = parser1.parse_args()
|
args = parser1.parse_args()
|
||||||
os.environ['CUDA_VISIBLE_DEVICES'] = args.GPU
|
os.environ['CUDA_VISIBLE_DEVICES'] = args.GPU#"1"
|
||||||
devices = torch.device('cuda:{}'.format(str(int(args.GPU))) if torch.cuda.is_available() else 'cpu')
|
try:
|
||||||
|
if "imagesAILabel" not in args.RawPath:
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
os.system("sudo chmod 777 -R {}".format(str(args.RawPath).replace("imagesAILabel","")))
|
||||||
|
except:
|
||||||
|
exit()
|
||||||
|
#devices = torch.device('cuda:{}'.format(str(int(args.GPU))) if torch.cuda.is_available() else 'cpu')
|
||||||
|
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||||
if not os.path.exists(args.RawPath.replace("imagesAILabel","NoResult")):
|
if not os.path.exists(args.RawPath.replace("imagesAILabel","NoResult")):
|
||||||
os.makedirs(args.RawPath.replace("imagesAILabel","NoResult"))
|
os.makedirs(args.RawPath.replace("imagesAILabel","NoResult"))
|
||||||
if not os.path.exists(args.RawPath.replace("imagesAILabel","YesResult")):
|
if not os.path.exists(args.RawPath.replace("imagesAILabel","JPEGImages")):
|
||||||
os.makedirs(args.RawPath.replace("imagesAILabel","YesResult"))
|
os.makedirs(args.RawPath.replace("imagesAILabel","JPEGImages"))
|
||||||
if not os.path.exists(args.RawPath.replace("imagesAILabel","LabelXML")):
|
if not os.path.exists(args.RawPath.replace("imagesAILabel","Annotations")):
|
||||||
os.makedirs(args.RawPath.replace("imagesAILabel","LabelXML"))
|
os.makedirs(args.RawPath.replace("imagesAILabel","Annotations"))
|
||||||
Detection = Detection_Y5(device=devices,root=" ", detect_thresh=args.thresh,DetWeight=args.Weight)
|
if not os.path.exists(args.RawPath.replace("imagesAILabel", "BrokenImage")):
|
||||||
|
os.makedirs(args.RawPath.replace("imagesAILabel", "BrokenImage"))
|
||||||
|
|
||||||
|
Detection = Detection_Y5(device=devices,root=" ", detect_thresh=args.thresh)
|
||||||
|
FineGrained = Fine_grained_T(device=devices)
|
||||||
imagepaths = os.listdir(args.RawPath)
|
imagepaths = os.listdir(args.RawPath)
|
||||||
for imagepath in tqdm(imagepaths):
|
for imagepath in tqdm(imagepaths):
|
||||||
imagepath = os.path.join(args.RawPath,imagepath)
|
imagepath = os.path.join(args.RawPath,imagepath)
|
||||||
Detection.AIlabel(imagepath)
|
Detection.AIlabelFine(imagepath,FineGrained)
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
import os,argparse,torch
|
||||||
|
from System_I import Detection_Y5,Fine_grained_T
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
#os.environ['CUDA_VISIBLE_DEVICES'] = "1"
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser1 = argparse.ArgumentParser(description="AILabel")
|
||||||
|
parser1.add_argument("--RawPath", default='/data/DataManagement/Project/temp/OrginalData', type=str,
|
||||||
|
help='RawPath.')
|
||||||
|
parser1.add_argument("--GPU", default="2", type=str,
|
||||||
|
help='GPU.')
|
||||||
|
parser1.add_argument("--thresh", default=0.4, type=float,
|
||||||
|
help='thresh.')
|
||||||
|
args = parser1.parse_args()
|
||||||
|
os.environ['CUDA_VISIBLE_DEVICES'] = args.GPU#"1"
|
||||||
|
try:
|
||||||
|
if "imagesAILabel" not in args.RawPath:
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
os.system("sudo chmod 777 -R {}".format(str(args.RawPath).replace("imagesAILabel","")))
|
||||||
|
except:
|
||||||
|
exit()
|
||||||
|
|
||||||
|
#devices = torch.device('cuda:{}'.format(str(int(args.GPU))) if torch.cuda.is_available() else 'cpu')
|
||||||
|
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||||
|
Detection = Detection_Y5(device=devices, root=" ", detect_thresh=args.thresh)
|
||||||
|
FineGrained = Fine_grained_T(device=devices)
|
||||||
|
for RawPaths in os.listdir(args.RawPath):
|
||||||
|
RawPath = os.path.join(args.RawPath,RawPaths,"imagesAILabel")
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel","NoResult")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel","NoResult"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel","JPEGImages")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel","JPEGImages"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel","Annotations")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel","Annotations"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "BrokenImage")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "BrokenImage"))
|
||||||
|
|
||||||
|
|
||||||
|
imagepaths = os.listdir(RawPath)
|
||||||
|
for imagepath in tqdm(imagepaths):
|
||||||
|
imagepath = os.path.join(args.RawPath,imagepath)
|
||||||
|
Detection.AIlabelFine(imagepath,FineGrained)
|
||||||
Executable
+66
@@ -0,0 +1,66 @@
|
|||||||
|
import os,argparse,torch
|
||||||
|
from System_I import Detection_Y5,Fine_grained_T,Fine_grained_mmclsonnx
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
#os.environ['CUDA_VISIBLE_DEVICES'] = "1"
|
||||||
|
"""###
|
||||||
|
输入:/根目录/imagesAI
|
||||||
|
-------文件夹1
|
||||||
|
------NoResult 存放无结果图片
|
||||||
|
------BrokenImage 存放损坏图片
|
||||||
|
------imagesAILabel 存放待处理图片
|
||||||
|
------Annotations 存放识别结果xml标签文件
|
||||||
|
------JPEGImages 备份原图原图
|
||||||
|
------result 存放可视化结果图片
|
||||||
|
-------文件夹2
|
||||||
|
------NoResult 存放无结果图片
|
||||||
|
------BrokenImage 存放损坏图片
|
||||||
|
------imagesAILabel 存放待处理图片
|
||||||
|
------Annotations 存放识别结果xml标签文件
|
||||||
|
------JPEGImages 备份原图原图
|
||||||
|
------result 存放可视化结果图片
|
||||||
|
|
||||||
|
###"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser1 = argparse.ArgumentParser(description="AILabel")
|
||||||
|
parser1.add_argument("--RawPath", default='/data5/student/Hatcher/test/imagesAI', type=str,
|
||||||
|
help='RawPath.')
|
||||||
|
parser1.add_argument("--GPU", default="2", type=str,
|
||||||
|
help='GPU.') #本参数在该工程中不起作用,请修改common.py文件种的全局参数
|
||||||
|
parser1.add_argument("--thresh", default=0.4, type=float,
|
||||||
|
help='thresh.')
|
||||||
|
args = parser1.parse_args()
|
||||||
|
os.environ['CUDA_VISIBLE_DEVICES'] = args.GPU#"1"
|
||||||
|
try:
|
||||||
|
if "imagesAI" not in args.RawPath:
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
os.system("sudo chmod 777 -R {}".format(str(args.RawPath).replace("imagesAI","")))
|
||||||
|
except:
|
||||||
|
exit()
|
||||||
|
|
||||||
|
#devices = torch.device('cuda:{}'.format(str(int(args.GPU))) if torch.cuda.is_available() else 'cpu')
|
||||||
|
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||||
|
Detection = Detection_Y5(device=devices, root=" ", detect_thresh=args.thresh)
|
||||||
|
FineGrained = Fine_grained_mmclsonnx(device=devices)
|
||||||
|
for RawPaths in os.listdir(args.RawPath):
|
||||||
|
RawPath = os.path.join(args.RawPath,RawPaths,"imagesAILabel")
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel","NoResult")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel","NoResult"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel","JPEGImages")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel","JPEGImages"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel","Annotations")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel","Annotations"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "BrokenImage")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "BrokenImage"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "result")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "result"))
|
||||||
|
|
||||||
|
|
||||||
|
imagepaths = os.listdir(RawPath)
|
||||||
|
for imagepath in tqdm(imagepaths):
|
||||||
|
imagepath = os.path.join(RawPath,imagepath)
|
||||||
|
Detection.AIlabelFine(imagepath,FineGrained)
|
||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import torch
|
||||||
|
from System_zwq import Detection_Y5, Fine_grained_T
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
#os.environ['CUDA_VISIBLE_DEVICES'] = "1"
|
||||||
|
|
||||||
|
def oneFolder():
|
||||||
|
RawPath = os.path.join(args.RawPath, "imagesAILabel")
|
||||||
|
os.system("sudo chmod 777 -R {}".format(str(args.RawPath)))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "NoResult")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "NoResult"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "JPEGImages")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "JPEGImages"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "Annotations")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "Annotations"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "BrokenImage")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "BrokenImage"))
|
||||||
|
|
||||||
|
imagepaths = os.listdir(RawPath)
|
||||||
|
for imagepath in tqdm(imagepaths, ncols=120, ascii=True, desc=RawPath + " images"):
|
||||||
|
imagepath = os.path.join(RawPath, imagepath)
|
||||||
|
Detection.AIlabelFine(imagepath, FineGrained)
|
||||||
|
|
||||||
|
|
||||||
|
def manyFolder():
|
||||||
|
for RawPaths in tqdm(os.listdir(args.RawPath), ncols=120, ascii=True, desc="bird folders"):
|
||||||
|
# for RawPaths in os.listdir(args.RawPath):
|
||||||
|
RawPath = os.path.join(args.RawPath, RawPaths, "imagesAILabel")
|
||||||
|
# os.system("sudo chmod 777 -R {}".format(str(os.path.join(args.RawPath, RawPaths))))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "NoResult")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "NoResult"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "JPEGImages")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "JPEGImages"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "Annotations")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "Annotations"))
|
||||||
|
if not os.path.exists(RawPath.replace("imagesAILabel", "BrokenImage")):
|
||||||
|
os.makedirs(RawPath.replace("imagesAILabel", "BrokenImage"))
|
||||||
|
|
||||||
|
imagepaths = os.listdir(RawPath)
|
||||||
|
for imagepath in tqdm(imagepaths, ncols=120, ascii=True, desc=RawPaths + " images"):
|
||||||
|
# for imagepath in tqdm(imagepaths):
|
||||||
|
imagepath = os.path.join(RawPath, imagepath)
|
||||||
|
Detection.AIlabelFine(imagepath, FineGrained)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser1 = argparse.ArgumentParser(description="AILabel")
|
||||||
|
parser1.add_argument("RawPath", default='/data5/PartDataset/003 PYH/0014 20230411/', type=str, help='RawPath.')
|
||||||
|
parser1.add_argument("--GPU", default="7", type=str, help='GPU.')
|
||||||
|
parser1.add_argument("--thresh", default=0.4, type=float, help='thresh.')
|
||||||
|
args = parser1.parse_args()
|
||||||
|
# os.environ['CUDA_VISIBLE_DEVICES'] = "7" #args.GPU # "1"
|
||||||
|
|
||||||
|
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||||
|
Detection = Detection_Y5(device=devices, root=" ", detect_thresh=args.thresh)
|
||||||
|
FineGrained = Fine_grained_T(device=devices)
|
||||||
|
oneFolder()
|
||||||
|
# manyFolder()
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ from vediotoima import vedio_img
|
|||||||
from imatovideo import images_to_video,images_to_video_big
|
from imatovideo import images_to_video,images_to_video_big
|
||||||
from moviepy.editor import *
|
from moviepy.editor import *
|
||||||
import os
|
import os
|
||||||
os.environ['CUDA_VISIBLE_DEVICES'] = '3'
|
#os.environ['CUDA_VISIBLE_DEVICES'] = '3'
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
|
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
"""
|
||||||
|
FInename---->0001.Finename
|
||||||
|
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from tqdm import tqdm
|
||||||
|
BHG_chineseName = ['bird', 'Anser_albifrons', 'Fulica_atra', 'Egretta_garzetta', 'Platalea_leucorodia', 'Mergellus_albellus', 'Tringa_ochropus', 'Anser_indicus', 'Anas_zonorhyncha', 'Ardea_cinerea', 'Mareca_strepera', 'Mareca_penelope', 'Ardea_alba', 'Botaurus_stellaris', 'Anser_fabalis', 'Recurvirostra_avosetta', 'Podiceps_cristatus', 'Vanellus_vanellus', 'Aythya_fuligula', 'Ciconia_nigra', 'Gallinula_chloropus', 'Chroicocephalus_ridibundus', 'Anser_cygnoides', 'Grus_grus', 'Anser_anser', 'Anas_crecca', 'Anas_platyrhynchos', 'Spatula_clypeata', 'Phalacrocorax_carbo', 'Mergus_merganser', 'Tadorna_tadorna', 'Bucephala_clangula', 'Gallinago_gallinago', 'Anser_erythropus', 'Cygnus_columbianus', 'Anas_acuta', 'Ardea_intermedia', 'Zosterops_simplex', 'Acridotheres_cristatellus', 'Circus_spilonotus', 'Motacilla_alba', 'Aythya_nyroca', 'Ardea_purpurea', 'Ardeola_bacchus', 'Upupa_epops', 'Himantopus_himantopus', 'Milvus_migrans', 'Falco_tinnunculus', 'Sibirionetta_formosa', 'Phasianus_colchicus', 'Picus_canus', 'Vanellus_cinereus', 'Actitis_hypoleucos', 'Ixobrychus_cinnamomeus', 'Mareca_falcata', 'Bubulcus_coromandus', 'Alcedo_atthis', 'Accipiter_nisus', 'Pica_serica', 'Nycticorax_nycticorax', 'Aix_galericulata', 'Lanius_schach', 'Mergus_serrator', 'Sterna_hirundo', 'Sternula_antillarum', 'Scolopax_rusticola', 'Pernis_ptilorhynchus', 'Cygnus_cygnus', 'Otis_tarda', 'Zapornia_pusilla', 'Streptopelia_orientalis', 'Aythya_valisineria', 'Accipiter_gularis', 'Buteo_japonicus', 'Accipiter_virgatus', 'Buteo_lagopus', 'Streptopelia_decaocto', 'Circus_aeruginosus', 'Circus_cyaneus', 'Spatula_querquedula', 'Pelecanus_onocrotalus', 'Aythya_ferina', 'Accipiter_gentilis', 'Larus_cachinnans', 'Rallus_aquaticus', 'Netta_rufina', 'Calidris_falcinellus', 'Bubo_bubo', 'Coturnix_japonica', 'Pandion_haliaetus', 'Ixobrychus_sinensis', 'Grus_nigricollis', 'Tachybaptus_ruficollis', 'Tadorna_ferruginea', 'Leucogeranus_leucogeranus', 'Grus_monacha', 'Antigone_vipio', 'Cyanopica_cyanus', 'Passer_montanus', 'Hirundo_rustica']
|
||||||
|
rootPath = "/data/DataManagement/Project/CAS_ProjectBird/CMB_ProjectBird0615/OrginalData"
|
||||||
|
os.system("sudo chmod 777 -R {}".format(rootPath))
|
||||||
|
for path in tqdm(os.listdir(rootPath),ncols=80, ascii=True, desc="change name"):
|
||||||
|
source = os.path.join(rootPath,path)
|
||||||
|
dest = os.path.join(rootPath,"{}.{}".format(str(BHG_chineseName.index(path)).zfill(4),str(path)))
|
||||||
|
os.rename(source,dest)
|
||||||
+1
-1
@@ -36,7 +36,7 @@ def GYD_RedCameraDeal (root,DetectionCalss,Fine_grainedClass):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.environ['CUDA_VISIBLE_DEVICES'] = '4'
|
#os.environ['CUDA_VISIBLE_DEVICES'] = '4'
|
||||||
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||||
root = "/data1/Hatcher/ftp"
|
root = "/data1/Hatcher/ftp"
|
||||||
Detection1 = Detection_S(devices, root)
|
Detection1 = Detection_S(devices, root)
|
||||||
|
|||||||
Executable
+1763
File diff suppressed because it is too large.
Load diff
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['NewDetAiLabelFine.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
optimize=0,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='NewDetAiLabelFine',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
Executable
+1717
File diff suppressed because it is too large.
Load diff
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
import os
|
||||||
|
in_path = "/data/DataManagement/Project/001_cuihu/CAS_CuihuBird/CDB_Cuihubird/ImageSets/Main/test.txt"
|
||||||
|
out_path = "/data/DataManagement/Project/001_cuihu/CAS_CuihuBird/CDB_Cuihubird/ImageSets/Main/test1.txt"
|
||||||
|
def change(in_path,out_path):
|
||||||
|
new = []
|
||||||
|
f = open(in_path,'r')
|
||||||
|
lines = f.readlines()
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip('\n')
|
||||||
|
line = "./images/{}.jpg".format(line)+" "+"./Annotations/{}.xml".format(line)+"\n"
|
||||||
|
new.append(line)
|
||||||
|
# while(line):
|
||||||
|
# line = f.readline().replace("\n","")
|
||||||
|
# line = "./images/{}".format(line) + " " + "./Annotations/{}".format(line) +"\n"
|
||||||
|
# print(line)
|
||||||
|
# new.append(line)
|
||||||
|
handle = open(out_path, 'a')
|
||||||
|
handle.writelines(new)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
change(in_path,out_path)
|
||||||
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
import os
|
||||||
|
import tqdm
|
||||||
|
import cv2
|
||||||
|
from PIL import Image
|
||||||
|
result_path = "E:/2021.10.15-wettrain/results/"
|
||||||
|
GT_path = "E:/2021.10.15-wettrain/result_GT/"
|
||||||
|
image_path ="E:/2021.10.15-wettrain/images/"
|
||||||
|
image = []
|
||||||
|
for j in range(14):
|
||||||
|
img = []
|
||||||
|
for i in range(60):
|
||||||
|
img.append(Image.open("/data/DataManagement/Project/001_cuihu/CAS_CuihuBird/CDB_Cuihubird/images/train/0010700285.jpg").resize((1024,768)))
|
||||||
|
image.append(img)
|
||||||
|
def long_pic_paste(image):
|
||||||
|
image_result = Image.new(image[0][0].mode, ( 1024 * 60,14 * 768), 0xffffff)
|
||||||
|
for j in range(14):
|
||||||
|
for i in range(60):
|
||||||
|
print(j," ",i)
|
||||||
|
image_result.paste(image[j][i], (i*1024, j*768))
|
||||||
|
#image_result.resize((65500,8591))
|
||||||
|
image_result.save("./result.jpg")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
long_pic_paste(image)
|
||||||
+896
-615
File diff suppressed because it is too large.
Load diff
Executable
+1708
File diff suppressed because it is too large.
Load diff
Regular → Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
+546
-117
@@ -1,18 +1,31 @@
|
|||||||
# This file contains modules common to various models
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
"""
|
||||||
|
Common modules
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
import math
|
import math
|
||||||
|
import platform
|
||||||
|
import warnings
|
||||||
|
from collections import OrderedDict, namedtuple
|
||||||
|
from copy import copy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
|
||||||
sys.path.append("..")
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
import requests
|
import requests
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
import yaml
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from torch.cuda import amp
|
||||||
|
|
||||||
from Y5.utils.datasets import letterbox
|
from utils.dataloaders import exif_transpose, letterbox
|
||||||
from Y5.utils.general import non_max_suppression, make_divisible, scale_coords, xyxy2xywh
|
from utils.general import (LOGGER, check_requirements, check_suffix, check_version, colorstr, increment_path,
|
||||||
from Y5.utils.plots import color_list, plot_one_box
|
make_divisible, non_max_suppression, scale_coords, xywh2xyxy, xyxy2xywh)
|
||||||
|
from utils.plots import Annotator, colors, save_one_box
|
||||||
|
from utils.torch_utils import copy_attr, time_sync
|
||||||
|
|
||||||
|
|
||||||
def autopad(k, p=None): # kernel, padding
|
def autopad(k, p=None): # kernel, padding
|
||||||
@@ -22,15 +35,10 @@ def autopad(k, p=None): # kernel, padding
|
|||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def DWConv(c1, c2, k=1, s=1, act=True):
|
|
||||||
# Depthwise convolution
|
|
||||||
return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
|
|
||||||
|
|
||||||
|
|
||||||
class Conv(nn.Module):
|
class Conv(nn.Module):
|
||||||
# Standard convolution
|
# Standard convolution
|
||||||
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
super(Conv, self).__init__()
|
super().__init__()
|
||||||
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
|
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
|
||||||
self.bn = nn.BatchNorm2d(c2)
|
self.bn = nn.BatchNorm2d(c2)
|
||||||
self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
|
self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
|
||||||
@@ -38,14 +46,62 @@ class Conv(nn.Module):
|
|||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
return self.act(self.bn(self.conv(x)))
|
return self.act(self.bn(self.conv(x)))
|
||||||
|
|
||||||
def fuseforward(self, x):
|
def forward_fuse(self, x):
|
||||||
return self.act(self.conv(x))
|
return self.act(self.conv(x))
|
||||||
|
|
||||||
|
|
||||||
|
class DWConv(Conv):
|
||||||
|
# Depth-wise convolution class
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
|
super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
|
||||||
|
|
||||||
|
|
||||||
|
class DWConvTranspose2d(nn.ConvTranspose2d):
|
||||||
|
# Depth-wise transpose convolution class
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p1=0, p2=0): # ch_in, ch_out, kernel, stride, padding, padding_out
|
||||||
|
super().__init__(c1, c2, k, s, p1, p2, groups=math.gcd(c1, c2))
|
||||||
|
|
||||||
|
|
||||||
|
class TransformerLayer(nn.Module):
|
||||||
|
# Transformer layer https://arxiv.org/abs/2010.11929 (LayerNorm layers removed for better performance)
|
||||||
|
def __init__(self, c, num_heads):
|
||||||
|
super().__init__()
|
||||||
|
self.q = nn.Linear(c, c, bias=False)
|
||||||
|
self.k = nn.Linear(c, c, bias=False)
|
||||||
|
self.v = nn.Linear(c, c, bias=False)
|
||||||
|
self.ma = nn.MultiheadAttention(embed_dim=c, num_heads=num_heads)
|
||||||
|
self.fc1 = nn.Linear(c, c, bias=False)
|
||||||
|
self.fc2 = nn.Linear(c, c, bias=False)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
x = self.ma(self.q(x), self.k(x), self.v(x))[0] + x
|
||||||
|
x = self.fc2(self.fc1(x)) + x
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class TransformerBlock(nn.Module):
|
||||||
|
# Vision Transformer https://arxiv.org/abs/2010.11929
|
||||||
|
def __init__(self, c1, c2, num_heads, num_layers):
|
||||||
|
super().__init__()
|
||||||
|
self.conv = None
|
||||||
|
if c1 != c2:
|
||||||
|
self.conv = Conv(c1, c2)
|
||||||
|
self.linear = nn.Linear(c2, c2) # learnable position embedding
|
||||||
|
self.tr = nn.Sequential(*(TransformerLayer(c2, num_heads) for _ in range(num_layers)))
|
||||||
|
self.c2 = c2
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
if self.conv is not None:
|
||||||
|
x = self.conv(x)
|
||||||
|
b, _, w, h = x.shape
|
||||||
|
p = x.flatten(2).permute(2, 0, 1)
|
||||||
|
return self.tr(p + self.linear(p)).permute(1, 2, 0).reshape(b, self.c2, w, h)
|
||||||
|
|
||||||
|
|
||||||
class Bottleneck(nn.Module):
|
class Bottleneck(nn.Module):
|
||||||
# Standard bottleneck
|
# Standard bottleneck
|
||||||
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
|
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
|
||||||
super(Bottleneck, self).__init__()
|
super().__init__()
|
||||||
c_ = int(c2 * e) # hidden channels
|
c_ = int(c2 * e) # hidden channels
|
||||||
self.cv1 = Conv(c1, c_, 1, 1)
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
self.cv2 = Conv(c_, c2, 3, 1, g=g)
|
self.cv2 = Conv(c_, c2, 3, 1, g=g)
|
||||||
@@ -58,41 +114,86 @@ class Bottleneck(nn.Module):
|
|||||||
class BottleneckCSP(nn.Module):
|
class BottleneckCSP(nn.Module):
|
||||||
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
|
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
|
||||||
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
super(BottleneckCSP, self).__init__()
|
super().__init__()
|
||||||
c_ = int(c2 * e) # hidden channels
|
c_ = int(c2 * e) # hidden channels
|
||||||
self.cv1 = Conv(c1, c_, 1, 1)
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
|
self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
|
||||||
self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
|
self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
|
||||||
self.cv4 = Conv(2 * c_, c2, 1, 1)
|
self.cv4 = Conv(2 * c_, c2, 1, 1)
|
||||||
self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
|
self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
|
||||||
self.act = nn.LeakyReLU(0.1, inplace=True)
|
self.act = nn.SiLU()
|
||||||
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
|
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
y1 = self.cv3(self.m(self.cv1(x)))
|
y1 = self.cv3(self.m(self.cv1(x)))
|
||||||
y2 = self.cv2(x)
|
y2 = self.cv2(x)
|
||||||
return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
|
return self.cv4(self.act(self.bn(torch.cat((y1, y2), 1))))
|
||||||
|
|
||||||
|
|
||||||
|
class CrossConv(nn.Module):
|
||||||
|
# Cross Convolution Downsample
|
||||||
|
def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False):
|
||||||
|
# ch_in, ch_out, kernel, stride, groups, expansion, shortcut
|
||||||
|
super().__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, (1, k), (1, s))
|
||||||
|
self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g)
|
||||||
|
self.add = shortcut and c1 == c2
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
|
||||||
|
|
||||||
|
|
||||||
class C3(nn.Module):
|
class C3(nn.Module):
|
||||||
# CSP Bottleneck with 3 convolutions
|
# CSP Bottleneck with 3 convolutions
|
||||||
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
super(C3, self).__init__()
|
super().__init__()
|
||||||
c_ = int(c2 * e) # hidden channels
|
c_ = int(c2 * e) # hidden channels
|
||||||
self.cv1 = Conv(c1, c_, 1, 1)
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
self.cv2 = Conv(c1, c_, 1, 1)
|
self.cv2 = Conv(c1, c_, 1, 1)
|
||||||
self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
|
self.cv3 = Conv(2 * c_, c2, 1) # optional act=FReLU(c2)
|
||||||
self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
|
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
|
||||||
# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
|
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
|
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), 1))
|
||||||
|
|
||||||
|
|
||||||
|
class C3x(C3):
|
||||||
|
# C3 module with cross-convolutions
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
|
||||||
|
super().__init__(c1, c2, n, shortcut, g, e)
|
||||||
|
c_ = int(c2 * e)
|
||||||
|
self.m = nn.Sequential(*(CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)))
|
||||||
|
|
||||||
|
|
||||||
|
class C3TR(C3):
|
||||||
|
# C3 module with TransformerBlock()
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
|
||||||
|
super().__init__(c1, c2, n, shortcut, g, e)
|
||||||
|
c_ = int(c2 * e)
|
||||||
|
self.m = TransformerBlock(c_, c_, 4, n)
|
||||||
|
|
||||||
|
|
||||||
|
class C3SPP(C3):
|
||||||
|
# C3 module with SPP()
|
||||||
|
def __init__(self, c1, c2, k=(5, 9, 13), n=1, shortcut=True, g=1, e=0.5):
|
||||||
|
super().__init__(c1, c2, n, shortcut, g, e)
|
||||||
|
c_ = int(c2 * e)
|
||||||
|
self.m = SPP(c_, c_, k)
|
||||||
|
|
||||||
|
|
||||||
|
class C3Ghost(C3):
|
||||||
|
# C3 module with GhostBottleneck()
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
|
||||||
|
super().__init__(c1, c2, n, shortcut, g, e)
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.m = nn.Sequential(*(GhostBottleneck(c_, c_) for _ in range(n)))
|
||||||
|
|
||||||
|
|
||||||
class SPP(nn.Module):
|
class SPP(nn.Module):
|
||||||
# Spatial pyramid pooling layer used in YOLOv3-SPP
|
# Spatial Pyramid Pooling (SPP) layer https://arxiv.org/abs/1406.4729
|
||||||
def __init__(self, c1, c2, k=(5, 9, 13)):
|
def __init__(self, c1, c2, k=(5, 9, 13)):
|
||||||
super(SPP, self).__init__()
|
super().__init__()
|
||||||
c_ = c1 // 2 # hidden channels
|
c_ = c1 // 2 # hidden channels
|
||||||
self.cv1 = Conv(c1, c_, 1, 1)
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
|
self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
|
||||||
@@ -100,21 +201,70 @@ class SPP(nn.Module):
|
|||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
x = self.cv1(x)
|
x = self.cv1(x)
|
||||||
return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
|
||||||
|
return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
|
||||||
|
|
||||||
|
|
||||||
|
class SPPF(nn.Module):
|
||||||
|
# Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
|
||||||
|
def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
|
||||||
|
super().__init__()
|
||||||
|
c_ = c1 // 2 # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, 1, 1)
|
||||||
|
self.cv2 = Conv(c_ * 4, c2, 1, 1)
|
||||||
|
self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
x = self.cv1(x)
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
|
||||||
|
y1 = self.m(x)
|
||||||
|
y2 = self.m(y1)
|
||||||
|
return self.cv2(torch.cat((x, y1, y2, self.m(y2)), 1))
|
||||||
|
|
||||||
|
|
||||||
class Focus(nn.Module):
|
class Focus(nn.Module):
|
||||||
# Focus wh information into c-space
|
# Focus wh information into c-space
|
||||||
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
super(Focus, self).__init__()
|
super().__init__()
|
||||||
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
|
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
|
||||||
# self.contract = Contract(gain=2)
|
# self.contract = Contract(gain=2)
|
||||||
|
|
||||||
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
|
def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
|
||||||
return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
|
return self.conv(torch.cat((x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]), 1))
|
||||||
# return self.conv(self.contract(x))
|
# return self.conv(self.contract(x))
|
||||||
|
|
||||||
|
|
||||||
|
class GhostConv(nn.Module):
|
||||||
|
# Ghost Convolution https://github.com/huawei-noah/ghostnet
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups
|
||||||
|
super().__init__()
|
||||||
|
c_ = c2 // 2 # hidden channels
|
||||||
|
self.cv1 = Conv(c1, c_, k, s, None, g, act)
|
||||||
|
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
y = self.cv1(x)
|
||||||
|
return torch.cat((y, self.cv2(y)), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class GhostBottleneck(nn.Module):
|
||||||
|
# Ghost Bottleneck https://github.com/huawei-noah/ghostnet
|
||||||
|
def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
|
||||||
|
super().__init__()
|
||||||
|
c_ = c2 // 2
|
||||||
|
self.conv = nn.Sequential(
|
||||||
|
GhostConv(c1, c_, 1, 1), # pw
|
||||||
|
DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw
|
||||||
|
GhostConv(c_, c2, 1, 1, act=False)) # pw-linear
|
||||||
|
self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), Conv(c1, c2, 1, 1,
|
||||||
|
act=False)) if s == 2 else nn.Identity()
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.conv(x) + self.shortcut(x)
|
||||||
|
|
||||||
|
|
||||||
class Contract(nn.Module):
|
class Contract(nn.Module):
|
||||||
# Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
|
# Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
|
||||||
def __init__(self, gain=2):
|
def __init__(self, gain=2):
|
||||||
@@ -122,11 +272,11 @@ class Contract(nn.Module):
|
|||||||
self.gain = gain
|
self.gain = gain
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
N, C, H, W = x.size() # assert (H / s == 0) and (W / s == 0), 'Indivisible gain'
|
b, c, h, w = x.size() # assert (h / s == 0) and (W / s == 0), 'Indivisible gain'
|
||||||
s = self.gain
|
s = self.gain
|
||||||
x = x.view(N, C, H // s, s, W // s, s) # x(1,64,40,2,40,2)
|
x = x.view(b, c, h // s, s, w // s, s) # x(1,64,40,2,40,2)
|
||||||
x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40)
|
x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40)
|
||||||
return x.view(N, C * s * s, H // s, W // s) # x(1,256,40,40)
|
return x.view(b, c * s * s, h // s, w // s) # x(1,256,40,40)
|
||||||
|
|
||||||
|
|
||||||
class Expand(nn.Module):
|
class Expand(nn.Module):
|
||||||
@@ -136,170 +286,449 @@ class Expand(nn.Module):
|
|||||||
self.gain = gain
|
self.gain = gain
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
N, C, H, W = x.size() # assert C / s ** 2 == 0, 'Indivisible gain'
|
b, c, h, w = x.size() # assert C / s ** 2 == 0, 'Indivisible gain'
|
||||||
s = self.gain
|
s = self.gain
|
||||||
x = x.view(N, s, s, C // s ** 2, H, W) # x(1,2,2,16,80,80)
|
x = x.view(b, s, s, c // s ** 2, h, w) # x(1,2,2,16,80,80)
|
||||||
x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # x(1,16,80,2,80,2)
|
x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # x(1,16,80,2,80,2)
|
||||||
return x.view(N, C // s ** 2, H * s, W * s) # x(1,16,160,160)
|
return x.view(b, c // s ** 2, h * s, w * s) # x(1,16,160,160)
|
||||||
|
|
||||||
|
|
||||||
class Concat(nn.Module):
|
class Concat(nn.Module):
|
||||||
# Concatenate a list of tensors along dimension
|
# Concatenate a list of tensors along dimension
|
||||||
def __init__(self, dimension=1):
|
def __init__(self, dimension=1):
|
||||||
super(Concat, self).__init__()
|
super().__init__()
|
||||||
self.d = dimension
|
self.d = dimension
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
return torch.cat(x, self.d)
|
return torch.cat(x, self.d)
|
||||||
|
|
||||||
|
|
||||||
class NMS(nn.Module):
|
class DetectMultiBackend(nn.Module):
|
||||||
# Non-Maximum Suppression (NMS) module
|
# YOLOv5 MultiBackend class for python inference on various backends
|
||||||
conf = 0.25 # confidence threshold
|
def __init__(self, weights='yolov5s.pt', device=torch.device('cpu'), dnn=False, data=None, fp16=False):
|
||||||
iou = 0.45 # IoU threshold
|
# Usage:
|
||||||
classes = None # (optional list) filter by class
|
# PyTorch: weights = *.pt
|
||||||
|
# TorchScript: *.torchscript
|
||||||
|
# ONNX Runtime: *.onnx
|
||||||
|
# ONNX OpenCV DNN: *.onnx with --dnn
|
||||||
|
# OpenVINO: *.xml
|
||||||
|
# CoreML: *.mlmodel
|
||||||
|
# TensorRT: *.engine
|
||||||
|
# TensorFlow SavedModel: *_saved_model
|
||||||
|
# TensorFlow GraphDef: *.pb
|
||||||
|
# TensorFlow Lite: *.tflite
|
||||||
|
# TensorFlow Edge TPU: *_edgetpu.tflite
|
||||||
|
from models.experimental import attempt_download, attempt_load # scoped to avoid circular import
|
||||||
|
|
||||||
def __init__(self):
|
super().__init__()
|
||||||
super(NMS, self).__init__()
|
w = str(weights[0] if isinstance(weights, list) else weights)
|
||||||
|
pt, jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs = self.model_type(w) # get backend
|
||||||
|
w = attempt_download(w) # download if not local
|
||||||
|
fp16 &= (pt or jit or onnx or engine) and device.type != 'cpu' # FP16
|
||||||
|
stride, names = 32, [f'class{i}' for i in range(1000)] # assign defaults
|
||||||
|
if data: # assign class names (optional)
|
||||||
|
with open(data, errors='ignore') as f:
|
||||||
|
names = yaml.safe_load(f)['names']
|
||||||
|
|
||||||
def forward(self, x):
|
if pt: # PyTorch
|
||||||
return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes)
|
model = attempt_load(weights if isinstance(weights, list) else w, device=device)
|
||||||
|
stride = max(int(model.stride.max()), 32) # model stride
|
||||||
|
names = model.module.names if hasattr(model, 'module') else model.names # get class names
|
||||||
|
model.half() if fp16 else model.float()
|
||||||
|
self.model = model # explicitly assign for to(), cpu(), cuda(), half()
|
||||||
|
elif jit: # TorchScript
|
||||||
|
LOGGER.info(f'Loading {w} for TorchScript inference...')
|
||||||
|
extra_files = {'config.txt': ''} # model metadata
|
||||||
|
model = torch.jit.load(w, _extra_files=extra_files)
|
||||||
|
model.half() if fp16 else model.float()
|
||||||
|
if extra_files['config.txt']:
|
||||||
|
d = json.loads(extra_files['config.txt']) # extra_files dict
|
||||||
|
stride, names = int(d['stride']), d['names']
|
||||||
|
elif dnn: # ONNX OpenCV DNN
|
||||||
|
LOGGER.info(f'Loading {w} for ONNX OpenCV DNN inference...')
|
||||||
|
check_requirements(('opencv-python>=4.5.4',))
|
||||||
|
net = cv2.dnn.readNetFromONNX(w)
|
||||||
|
elif onnx: # ONNX Runtime
|
||||||
|
LOGGER.info(f'Loading {w} for ONNX Runtime inference...')
|
||||||
|
cuda = torch.cuda.is_available()
|
||||||
|
check_requirements(('onnx', 'onnxruntime-gpu' if cuda else 'onnxruntime'))
|
||||||
|
import onnxruntime
|
||||||
|
providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
|
||||||
|
session = onnxruntime.InferenceSession(w, providers=providers)
|
||||||
|
meta = session.get_modelmeta().custom_metadata_map # metadata
|
||||||
|
if 'stride' in meta:
|
||||||
|
stride, names = int(meta['stride']), eval(meta['names'])
|
||||||
|
elif xml: # OpenVINO
|
||||||
|
LOGGER.info(f'Loading {w} for OpenVINO inference...')
|
||||||
|
check_requirements(('openvino',)) # requires openvino-dev: https://pypi.org/project/openvino-dev/
|
||||||
|
from openvino.runtime import Core
|
||||||
|
ie = Core()
|
||||||
|
if not Path(w).is_file(): # if not *.xml
|
||||||
|
w = next(Path(w).glob('*.xml')) # get *.xml file from *_openvino_model dir
|
||||||
|
network = ie.read_model(model=w, weights=Path(w).with_suffix('.bin'))
|
||||||
|
executable_network = ie.compile_model(model=network, device_name="CPU")
|
||||||
|
output_layer = next(iter(executable_network.outputs))
|
||||||
|
meta = Path(w).with_suffix('.yaml')
|
||||||
|
if meta.exists():
|
||||||
|
stride, names = self._load_metadata(meta) # load metadata
|
||||||
|
elif engine: # TensorRT
|
||||||
|
LOGGER.info(f'Loading {w} for TensorRT inference...')
|
||||||
|
import tensorrt as trt # https://developer.nvidia.com/nvidia-tensorrt-download
|
||||||
|
check_version(trt.__version__, '7.0.0', hard=True) # require tensorrt>=7.0.0
|
||||||
|
Binding = namedtuple('Binding', ('name', 'dtype', 'shape', 'data', 'ptr'))
|
||||||
|
logger = trt.Logger(trt.Logger.INFO)
|
||||||
|
with open(w, 'rb') as f, trt.Runtime(logger) as runtime:
|
||||||
|
model = runtime.deserialize_cuda_engine(f.read())
|
||||||
|
bindings = OrderedDict()
|
||||||
|
fp16 = False # default updated below
|
||||||
|
for index in range(model.num_bindings):
|
||||||
|
name = model.get_binding_name(index)
|
||||||
|
dtype = trt.nptype(model.get_binding_dtype(index))
|
||||||
|
shape = tuple(model.get_binding_shape(index))
|
||||||
|
data = torch.from_numpy(np.empty(shape, dtype=np.dtype(dtype))).to(device)
|
||||||
|
bindings[name] = Binding(name, dtype, shape, data, int(data.data_ptr()))
|
||||||
|
if model.binding_is_input(index) and dtype == np.float16:
|
||||||
|
fp16 = True
|
||||||
|
binding_addrs = OrderedDict((n, d.ptr) for n, d in bindings.items())
|
||||||
|
context = model.create_execution_context()
|
||||||
|
batch_size = bindings['images'].shape[0]
|
||||||
|
elif coreml: # CoreML
|
||||||
|
LOGGER.info(f'Loading {w} for CoreML inference...')
|
||||||
|
import coremltools as ct
|
||||||
|
model = ct.models.MLModel(w)
|
||||||
|
else: # TensorFlow (SavedModel, GraphDef, Lite, Edge TPU)
|
||||||
|
if saved_model: # SavedModel
|
||||||
|
LOGGER.info(f'Loading {w} for TensorFlow SavedModel inference...')
|
||||||
|
import tensorflow as tf
|
||||||
|
keras = False # assume TF1 saved_model
|
||||||
|
model = tf.keras.models.load_model(w) if keras else tf.saved_model.load(w)
|
||||||
|
elif pb: # GraphDef https://www.tensorflow.org/guide/migrate#a_graphpb_or_graphpbtxt
|
||||||
|
LOGGER.info(f'Loading {w} for TensorFlow GraphDef inference...')
|
||||||
|
import tensorflow as tf
|
||||||
|
|
||||||
|
def wrap_frozen_graph(gd, inputs, outputs):
|
||||||
|
x = tf.compat.v1.wrap_function(lambda: tf.compat.v1.import_graph_def(gd, name=""), []) # wrapped
|
||||||
|
ge = x.graph.as_graph_element
|
||||||
|
return x.prune(tf.nest.map_structure(ge, inputs), tf.nest.map_structure(ge, outputs))
|
||||||
|
|
||||||
|
gd = tf.Graph().as_graph_def() # graph_def
|
||||||
|
with open(w, 'rb') as f:
|
||||||
|
gd.ParseFromString(f.read())
|
||||||
|
frozen_func = wrap_frozen_graph(gd, inputs="x:0", outputs="Identity:0")
|
||||||
|
elif tflite or edgetpu: # https://www.tensorflow.org/lite/guide/python#install_tensorflow_lite_for_python
|
||||||
|
try: # https://coral.ai/docs/edgetpu/tflite-python/#update-existing-tf-lite-code-for-the-edge-tpu
|
||||||
|
from tflite_runtime.interpreter import Interpreter, load_delegate
|
||||||
|
except ImportError:
|
||||||
|
import tensorflow as tf
|
||||||
|
Interpreter, load_delegate = tf.lite.Interpreter, tf.lite.experimental.load_delegate,
|
||||||
|
if edgetpu: # Edge TPU https://coral.ai/software/#edgetpu-runtime
|
||||||
|
LOGGER.info(f'Loading {w} for TensorFlow Lite Edge TPU inference...')
|
||||||
|
delegate = {
|
||||||
|
'Linux': 'libedgetpu.so.1',
|
||||||
|
'Darwin': 'libedgetpu.1.dylib',
|
||||||
|
'Windows': 'edgetpu.dll'}[platform.system()]
|
||||||
|
interpreter = Interpreter(model_path=w, experimental_delegates=[load_delegate(delegate)])
|
||||||
|
else: # Lite
|
||||||
|
LOGGER.info(f'Loading {w} for TensorFlow Lite inference...')
|
||||||
|
interpreter = Interpreter(model_path=w) # load TFLite model
|
||||||
|
interpreter.allocate_tensors() # allocate
|
||||||
|
input_details = interpreter.get_input_details() # inputs
|
||||||
|
output_details = interpreter.get_output_details() # outputs
|
||||||
|
elif tfjs:
|
||||||
|
raise Exception('ERROR: YOLOv5 TF.js inference is not supported')
|
||||||
|
self.__dict__.update(locals()) # assign all variables to self
|
||||||
|
|
||||||
|
def forward(self, im, augment=False, visualize=False, val=False):
|
||||||
|
# YOLOv5 MultiBackend inference
|
||||||
|
b, ch, h, w = im.shape # batch, channel, height, width
|
||||||
|
if self.pt: # PyTorch
|
||||||
|
y = self.model(im, augment=augment, visualize=visualize)[0]
|
||||||
|
elif self.jit: # TorchScript
|
||||||
|
y = self.model(im)[0]
|
||||||
|
elif self.dnn: # ONNX OpenCV DNN
|
||||||
|
im = im.cpu().numpy() # torch to numpy
|
||||||
|
self.net.setInput(im)
|
||||||
|
y = self.net.forward()
|
||||||
|
elif self.onnx: # ONNX Runtime
|
||||||
|
im = im.cpu().numpy() # torch to numpy
|
||||||
|
y = self.session.run([self.session.get_outputs()[0].name], {self.session.get_inputs()[0].name: im})[0]
|
||||||
|
elif self.xml: # OpenVINO
|
||||||
|
im = im.cpu().numpy() # FP32
|
||||||
|
y = self.executable_network([im])[self.output_layer]
|
||||||
|
elif self.engine: # TensorRT
|
||||||
|
assert im.shape == self.bindings['images'].shape, (im.shape, self.bindings['images'].shape)
|
||||||
|
self.binding_addrs['images'] = int(im.data_ptr())
|
||||||
|
self.context.execute_v2(list(self.binding_addrs.values()))
|
||||||
|
y = self.bindings['output'].data
|
||||||
|
elif self.coreml: # CoreML
|
||||||
|
im = im.permute(0, 2, 3, 1).cpu().numpy() # torch BCHW to numpy BHWC shape(1,320,192,3)
|
||||||
|
im = Image.fromarray((im[0] * 255).astype('uint8'))
|
||||||
|
# im = im.resize((192, 320), Image.ANTIALIAS)
|
||||||
|
y = self.model.predict({'image': im}) # coordinates are xywh normalized
|
||||||
|
if 'confidence' in y:
|
||||||
|
box = xywh2xyxy(y['coordinates'] * [[w, h, w, h]]) # xyxy pixels
|
||||||
|
conf, cls = y['confidence'].max(1), y['confidence'].argmax(1).astype(np.float)
|
||||||
|
y = np.concatenate((box, conf.reshape(-1, 1), cls.reshape(-1, 1)), 1)
|
||||||
|
else:
|
||||||
|
k = 'var_' + str(sorted(int(k.replace('var_', '')) for k in y)[-1]) # output key
|
||||||
|
y = y[k] # output
|
||||||
|
else: # TensorFlow (SavedModel, GraphDef, Lite, Edge TPU)
|
||||||
|
im = im.permute(0, 2, 3, 1).cpu().numpy() # torch BCHW to numpy BHWC shape(1,320,192,3)
|
||||||
|
if self.saved_model: # SavedModel
|
||||||
|
y = (self.model(im, training=False) if self.keras else self.model(im)).numpy()
|
||||||
|
elif self.pb: # GraphDef
|
||||||
|
y = self.frozen_func(x=self.tf.constant(im)).numpy()
|
||||||
|
else: # Lite or Edge TPU
|
||||||
|
input, output = self.input_details[0], self.output_details[0]
|
||||||
|
int8 = input['dtype'] == np.uint8 # is TFLite quantized uint8 model
|
||||||
|
if int8:
|
||||||
|
scale, zero_point = input['quantization']
|
||||||
|
im = (im / scale + zero_point).astype(np.uint8) # de-scale
|
||||||
|
self.interpreter.set_tensor(input['index'], im)
|
||||||
|
self.interpreter.invoke()
|
||||||
|
y = self.interpreter.get_tensor(output['index'])
|
||||||
|
if int8:
|
||||||
|
scale, zero_point = output['quantization']
|
||||||
|
y = (y.astype(np.float32) - zero_point) * scale # re-scale
|
||||||
|
y[..., :4] *= [w, h, w, h] # xywh normalized to pixels
|
||||||
|
|
||||||
|
if isinstance(y, np.ndarray):
|
||||||
|
y = torch.tensor(y, device=self.device)
|
||||||
|
return (y, []) if val else y
|
||||||
|
|
||||||
|
def warmup(self, imgsz=(1, 3, 640, 640)):
|
||||||
|
# Warmup model by running inference once
|
||||||
|
warmup_types = self.pt, self.jit, self.onnx, self.engine, self.saved_model, self.pb
|
||||||
|
if any(warmup_types) and self.device.type != 'cpu':
|
||||||
|
im = torch.zeros(*imgsz, dtype=torch.half if self.fp16 else torch.float, device=self.device) # input
|
||||||
|
for _ in range(2 if self.jit else 1): #
|
||||||
|
self.forward(im) # warmup
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def model_type(p='path/to/model.pt'):
|
||||||
|
# Return model type from model path, i.e. path='path/to/model.onnx' -> type=onnx
|
||||||
|
from export import export_formats
|
||||||
|
suffixes = list(export_formats().Suffix) + ['.xml'] # export suffixes
|
||||||
|
check_suffix(p, suffixes) # checks
|
||||||
|
p = Path(p).name # eliminate trailing separators
|
||||||
|
pt, jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, xml2 = (s in p for s in suffixes)
|
||||||
|
xml |= xml2 # *_openvino_model or *.xml
|
||||||
|
tflite &= not edgetpu # *.tflite
|
||||||
|
return pt, jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _load_metadata(f='path/to/meta.yaml'):
|
||||||
|
# Load metadata from meta.yaml if it exists
|
||||||
|
with open(f, errors='ignore') as f:
|
||||||
|
d = yaml.safe_load(f)
|
||||||
|
return d['stride'], d['names'] # assign stride, names
|
||||||
|
|
||||||
|
|
||||||
class autoShape(nn.Module):
|
class AutoShape(nn.Module):
|
||||||
# input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS
|
# YOLOv5 input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS
|
||||||
img_size = 640 # inference size (pixels)
|
|
||||||
conf = 0.25 # NMS confidence threshold
|
conf = 0.25 # NMS confidence threshold
|
||||||
iou = 0.45 # NMS IoU threshold
|
iou = 0.45 # NMS IoU threshold
|
||||||
classes = None # (optional list) filter by class
|
agnostic = False # NMS class-agnostic
|
||||||
|
multi_label = False # NMS multiple labels per box
|
||||||
|
classes = None # (optional list) filter by class, i.e. = [0, 15, 16] for COCO persons, cats and dogs
|
||||||
|
max_det = 1000 # maximum number of detections per image
|
||||||
|
amp = False # Automatic Mixed Precision (AMP) inference
|
||||||
|
|
||||||
def __init__(self, model):
|
def __init__(self, model, verbose=True):
|
||||||
super(autoShape, self).__init__()
|
super().__init__()
|
||||||
|
if verbose:
|
||||||
|
LOGGER.info('Adding AutoShape... ')
|
||||||
|
copy_attr(self, model, include=('yaml', 'nc', 'hyp', 'names', 'stride', 'abc'), exclude=()) # copy attributes
|
||||||
|
self.dmb = isinstance(model, DetectMultiBackend) # DetectMultiBackend() instance
|
||||||
|
self.pt = not self.dmb or model.pt # PyTorch model
|
||||||
self.model = model.eval()
|
self.model = model.eval()
|
||||||
|
|
||||||
def autoshape(self):
|
def _apply(self, fn):
|
||||||
print('autoShape already enabled, skipping... ') # model already converted to model.autoshape()
|
# Apply to(), cpu(), cuda(), half() to model tensors that are not parameters or registered buffers
|
||||||
|
self = super()._apply(fn)
|
||||||
|
if self.pt:
|
||||||
|
m = self.model.model.model[-1] if self.dmb else self.model.model[-1] # Detect()
|
||||||
|
m.stride = fn(m.stride)
|
||||||
|
m.grid = list(map(fn, m.grid))
|
||||||
|
if isinstance(m.anchor_grid, list):
|
||||||
|
m.anchor_grid = list(map(fn, m.anchor_grid))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@torch.no_grad()
|
||||||
def forward(self, imgs, size=640, augment=False, profile=False):
|
def forward(self, imgs, size=640, augment=False, profile=False):
|
||||||
# Inference from various sources. For height=720, width=1280, RGB images example inputs are:
|
# Inference from various sources. For height=640, width=1280, RGB images example inputs are:
|
||||||
# filename: imgs = 'data/samples/zidane.jpg'
|
# file: imgs = 'data/images/zidane.jpg' # str or PosixPath
|
||||||
# URI: = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg'
|
# URI: = 'https://ultralytics.com/images/zidane.jpg'
|
||||||
# OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(720,1280,3)
|
# OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(640,1280,3)
|
||||||
# PIL: = Image.open('image.jpg') # HWC x(720,1280,3)
|
# PIL: = Image.open('image.jpg') or ImageGrab.grab() # HWC x(640,1280,3)
|
||||||
# numpy: = np.zeros((720,1280,3)) # HWC
|
# numpy: = np.zeros((640,1280,3)) # HWC
|
||||||
# torch: = torch.zeros(16,3,720,1280) # BCHW
|
# torch: = torch.zeros(16,3,320,640) # BCHW (scaled to size=640, 0-1 values)
|
||||||
# multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images
|
# multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images
|
||||||
|
|
||||||
p = next(self.model.parameters()) # for device and type
|
t = [time_sync()]
|
||||||
|
p = next(self.model.parameters()) if self.pt else torch.zeros(1, device=self.model.device) # for device, type
|
||||||
|
autocast = self.amp and (p.device.type != 'cpu') # Automatic Mixed Precision (AMP) inference
|
||||||
if isinstance(imgs, torch.Tensor): # torch
|
if isinstance(imgs, torch.Tensor): # torch
|
||||||
return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference
|
with amp.autocast(autocast):
|
||||||
|
return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference
|
||||||
|
|
||||||
# Pre-process
|
# Pre-process
|
||||||
n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images
|
n, imgs = (len(imgs), list(imgs)) if isinstance(imgs, (list, tuple)) else (1, [imgs]) # number, list of images
|
||||||
shape0, shape1, files = [], [], [] # image and inference shapes, filenames
|
shape0, shape1, files = [], [], [] # image and inference shapes, filenames
|
||||||
for i, im in enumerate(imgs):
|
for i, im in enumerate(imgs):
|
||||||
if isinstance(im, str): # filename or uri
|
f = f'image{i}' # filename
|
||||||
im, f = Image.open(requests.get(im, stream=True).raw if im.startswith('http') else im), im # open
|
if isinstance(im, (str, Path)): # filename or uri
|
||||||
im.filename = f # for uri
|
im, f = Image.open(requests.get(im, stream=True).raw if str(im).startswith('http') else im), im
|
||||||
files.append(Path(im.filename).with_suffix('.jpg').name if isinstance(im, Image.Image) else f'image{i}.jpg')
|
im = np.asarray(exif_transpose(im))
|
||||||
im = np.array(im) # to numpy
|
elif isinstance(im, Image.Image): # PIL Image
|
||||||
|
im, f = np.asarray(exif_transpose(im)), getattr(im, 'filename', f) or f
|
||||||
|
files.append(Path(f).with_suffix('.jpg').name)
|
||||||
if im.shape[0] < 5: # image in CHW
|
if im.shape[0] < 5: # image in CHW
|
||||||
im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1)
|
im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1)
|
||||||
im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3) # enforce 3ch input
|
im = im[..., :3] if im.ndim == 3 else np.tile(im[..., None], 3) # enforce 3ch input
|
||||||
s = im.shape[:2] # HWC
|
s = im.shape[:2] # HWC
|
||||||
shape0.append(s) # image shape
|
shape0.append(s) # image shape
|
||||||
g = (size / max(s)) # gain
|
g = (size / max(s)) # gain
|
||||||
shape1.append([y * g for y in s])
|
shape1.append([y * g for y in s])
|
||||||
imgs[i] = im # update
|
imgs[i] = im if im.data.contiguous else np.ascontiguousarray(im) # update
|
||||||
shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape
|
shape1 = [make_divisible(x, self.stride) if self.pt else size for x in np.array(shape1).max(0)] # inf shape
|
||||||
x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad
|
x = [letterbox(im, shape1, auto=False)[0] for im in imgs] # pad
|
||||||
x = np.stack(x, 0) if n > 1 else x[0][None] # stack
|
x = np.ascontiguousarray(np.array(x).transpose((0, 3, 1, 2))) # stack and BHWC to BCHW
|
||||||
x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW
|
x = torch.from_numpy(x).to(p.device).type_as(p) / 255 # uint8 to fp16/32
|
||||||
x = torch.from_numpy(x).to(p.device).type_as(p) / 255. # uint8 to fp16/32
|
t.append(time_sync())
|
||||||
|
|
||||||
# Inference
|
with amp.autocast(autocast):
|
||||||
with torch.no_grad():
|
# Inference
|
||||||
y = self.model(x, augment, profile)[0] # forward
|
y = self.model(x, augment, profile) # forward
|
||||||
y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS
|
t.append(time_sync())
|
||||||
|
|
||||||
# Post-process
|
# Post-process
|
||||||
for i in range(n):
|
y = non_max_suppression(y if self.dmb else y[0],
|
||||||
scale_coords(shape1, y[i][:, :4], shape0[i])
|
self.conf,
|
||||||
|
self.iou,
|
||||||
|
self.classes,
|
||||||
|
self.agnostic,
|
||||||
|
self.multi_label,
|
||||||
|
max_det=self.max_det) # NMS
|
||||||
|
for i in range(n):
|
||||||
|
scale_coords(shape1, y[i][:, :4], shape0[i])
|
||||||
|
|
||||||
return Detections(imgs, y, files, self.names)
|
t.append(time_sync())
|
||||||
|
return Detections(imgs, y, files, t, self.names, x.shape)
|
||||||
|
|
||||||
|
|
||||||
class Detections:
|
class Detections:
|
||||||
# detections class for YOLOv5 inference results
|
# YOLOv5 detections class for inference results
|
||||||
def __init__(self, imgs, pred, files, names=None):
|
def __init__(self, imgs, pred, files, times=(0, 0, 0, 0), names=None, shape=None):
|
||||||
super(Detections, self).__init__()
|
super().__init__()
|
||||||
d = pred[0].device # device
|
d = pred[0].device # device
|
||||||
gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations
|
gn = [torch.tensor([*(im.shape[i] for i in [1, 0, 1, 0]), 1, 1], device=d) for im in imgs] # normalizations
|
||||||
self.imgs = imgs # list of images as numpy arrays
|
self.imgs = imgs # list of images as numpy arrays
|
||||||
self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
|
self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
|
||||||
self.names = names # class names
|
self.names = names # class names
|
||||||
self.files = files # image filenames
|
self.files = files # image filenames
|
||||||
|
self.times = times # profiling times
|
||||||
self.xyxy = pred # xyxy pixels
|
self.xyxy = pred # xyxy pixels
|
||||||
self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
|
self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
|
||||||
self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
|
self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
|
||||||
self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
|
self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
|
||||||
self.n = len(self.pred)
|
self.n = len(self.pred) # number of images (batch size)
|
||||||
|
self.t = tuple((times[i + 1] - times[i]) * 1000 / self.n for i in range(3)) # timestamps (ms)
|
||||||
|
self.s = shape # inference BCHW shape
|
||||||
|
|
||||||
def display(self, pprint=False, show=False, save=False, render=False, save_dir=''):
|
def display(self, pprint=False, show=False, save=False, crop=False, render=False, labels=True, save_dir=Path('')):
|
||||||
colors = color_list()
|
crops = []
|
||||||
for i, (img, pred) in enumerate(zip(self.imgs, self.pred)):
|
for i, (im, pred) in enumerate(zip(self.imgs, self.pred)):
|
||||||
str = f'image {i + 1}/{len(self.pred)}: {img.shape[0]}x{img.shape[1]} '
|
s = f'image {i + 1}/{len(self.pred)}: {im.shape[0]}x{im.shape[1]} ' # string
|
||||||
if pred is not None:
|
if pred.shape[0]:
|
||||||
for c in pred[:, -1].unique():
|
for c in pred[:, -1].unique():
|
||||||
n = (pred[:, -1] == c).sum() # detections per class
|
n = (pred[:, -1] == c).sum() # detections per class
|
||||||
str += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string
|
s += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string
|
||||||
if show or save or render:
|
if show or save or render or crop:
|
||||||
for *box, conf, cls in pred: # xyxy, confidence, class
|
annotator = Annotator(im, example=str(self.names))
|
||||||
|
for *box, conf, cls in reversed(pred): # xyxy, confidence, class
|
||||||
label = f'{self.names[int(cls)]} {conf:.2f}'
|
label = f'{self.names[int(cls)]} {conf:.2f}'
|
||||||
plot_one_box(box, img, label=label, color=colors[int(cls) % 10])
|
if crop:
|
||||||
img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np
|
file = save_dir / 'crops' / self.names[int(cls)] / self.files[i] if save else None
|
||||||
|
crops.append({
|
||||||
|
'box': box,
|
||||||
|
'conf': conf,
|
||||||
|
'cls': cls,
|
||||||
|
'label': label,
|
||||||
|
'im': save_one_box(box, im, file=file, save=save)})
|
||||||
|
else: # all others
|
||||||
|
annotator.box_label(box, label if labels else '', color=colors(cls))
|
||||||
|
im = annotator.im
|
||||||
|
else:
|
||||||
|
s += '(no detections)'
|
||||||
|
|
||||||
|
im = Image.fromarray(im.astype(np.uint8)) if isinstance(im, np.ndarray) else im # from np
|
||||||
if pprint:
|
if pprint:
|
||||||
print(str.rstrip(', '))
|
print(s.rstrip(', '))
|
||||||
if show:
|
if show:
|
||||||
img.show(self.files[i]) # show
|
im.show(self.files[i]) # show
|
||||||
if save:
|
if save:
|
||||||
f = Path(save_dir) / self.files[i]
|
f = self.files[i]
|
||||||
img.save(f) # save
|
im.save(save_dir / f) # save
|
||||||
print(f"{'Saving' * (i == 0)} {f},", end='' if i < self.n - 1 else ' done.\n')
|
if i == self.n - 1:
|
||||||
|
LOGGER.info(f"Saved {self.n} image{'s' * (self.n > 1)} to {colorstr('bold', save_dir)}")
|
||||||
if render:
|
if render:
|
||||||
self.imgs[i] = np.asarray(img)
|
self.imgs[i] = np.asarray(im)
|
||||||
|
if crop:
|
||||||
|
if save:
|
||||||
|
LOGGER.info(f'Saved results to {save_dir}\n')
|
||||||
|
return crops
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
self.display(pprint=True) # print results
|
self.display(pprint=True) # print results
|
||||||
|
print(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {tuple(self.s)}' % self.t)
|
||||||
|
|
||||||
def show(self):
|
def show(self, labels=True):
|
||||||
self.display(show=True) # show results
|
self.display(show=True, labels=labels) # show results
|
||||||
|
|
||||||
def save(self, save_dir='results/'):
|
def save(self, labels=True, save_dir='runs/detect/exp'):
|
||||||
Path(save_dir).mkdir(exist_ok=True)
|
save_dir = increment_path(save_dir, exist_ok=save_dir != 'runs/detect/exp', mkdir=True) # increment save_dir
|
||||||
self.display(save=True, save_dir=save_dir) # save results
|
self.display(save=True, labels=labels, save_dir=save_dir) # save results
|
||||||
|
|
||||||
def render(self):
|
def crop(self, save=True, save_dir='runs/detect/exp'):
|
||||||
self.display(render=True) # render results
|
save_dir = increment_path(save_dir, exist_ok=save_dir != 'runs/detect/exp', mkdir=True) if save else None
|
||||||
|
return self.display(crop=True, save=save, save_dir=save_dir) # crop results
|
||||||
|
|
||||||
|
def render(self, labels=True):
|
||||||
|
self.display(render=True, labels=labels) # render results
|
||||||
return self.imgs
|
return self.imgs
|
||||||
|
|
||||||
def __len__(self):
|
def pandas(self):
|
||||||
return self.n
|
# return detections as pandas DataFrames, i.e. print(results.pandas().xyxy[0])
|
||||||
|
new = copy(self) # return copy
|
||||||
|
ca = 'xmin', 'ymin', 'xmax', 'ymax', 'confidence', 'class', 'name' # xyxy columns
|
||||||
|
cb = 'xcenter', 'ycenter', 'width', 'height', 'confidence', 'class', 'name' # xywh columns
|
||||||
|
for k, c in zip(['xyxy', 'xyxyn', 'xywh', 'xywhn'], [ca, ca, cb, cb]):
|
||||||
|
a = [[x[:5] + [int(x[5]), self.names[int(x[5])]] for x in x.tolist()] for x in getattr(self, k)] # update
|
||||||
|
setattr(new, k, [pd.DataFrame(x, columns=c) for x in a])
|
||||||
|
return new
|
||||||
|
|
||||||
def tolist(self):
|
def tolist(self):
|
||||||
# return a list of Detections objects, i.e. 'for result in results.tolist():'
|
# return a list of Detections objects, i.e. 'for result in results.tolist():'
|
||||||
x = [Detections([self.imgs[i]], [self.pred[i]], self.names) for i in range(self.n)]
|
r = range(self.n) # iterable
|
||||||
for d in x:
|
x = [Detections([self.imgs[i]], [self.pred[i]], [self.files[i]], self.times, self.names, self.s) for i in r]
|
||||||
for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
|
# for d in x:
|
||||||
setattr(d, k, getattr(d, k)[0]) # pop out of list
|
# for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
|
||||||
|
# setattr(d, k, getattr(d, k)[0]) # pop out of list
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.n # override len(results)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
self.print() # override print(results)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
class Classify(nn.Module):
|
class Classify(nn.Module):
|
||||||
# Classification head, i.e. x(b,c1,20,20) to x(b,c2)
|
# Classification head, i.e. x(b,c1,20,20) to x(b,c2)
|
||||||
def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups
|
||||||
super(Classify, self).__init__()
|
super().__init__()
|
||||||
self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1)
|
self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1)
|
||||||
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g) # to x(b,c2,1,1)
|
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g) # to x(b,c2,1,1)
|
||||||
self.flat = nn.Flatten()
|
self.flat = nn.Flatten()
|
||||||
|
|||||||
+46
-77
@@ -1,36 +1,25 @@
|
|||||||
# This file contains experimental modules
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
"""
|
||||||
|
Experimental modules
|
||||||
|
"""
|
||||||
|
import math
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
|
from models.common import Conv
|
||||||
from Y5.models.common import Conv, DWConv
|
from utils.downloads import attempt_download
|
||||||
from Y5.utils.google_utils import attempt_download
|
|
||||||
|
|
||||||
|
|
||||||
class CrossConv(nn.Module):
|
|
||||||
# Cross Convolution Downsample
|
|
||||||
def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False):
|
|
||||||
# ch_in, ch_out, kernel, stride, groups, expansion, shortcut
|
|
||||||
super(CrossConv, self).__init__()
|
|
||||||
c_ = int(c2 * e) # hidden channels
|
|
||||||
self.cv1 = Conv(c1, c_, (1, k), (1, s))
|
|
||||||
self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g)
|
|
||||||
self.add = shortcut and c1 == c2
|
|
||||||
|
|
||||||
def forward(self, x):
|
|
||||||
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
|
|
||||||
|
|
||||||
|
|
||||||
class Sum(nn.Module):
|
class Sum(nn.Module):
|
||||||
# Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070
|
# Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070
|
||||||
def __init__(self, n, weight=False): # n: number of inputs
|
def __init__(self, n, weight=False): # n: number of inputs
|
||||||
super(Sum, self).__init__()
|
super().__init__()
|
||||||
self.weight = weight # apply weights boolean
|
self.weight = weight # apply weights boolean
|
||||||
self.iter = range(n - 1) # iter object
|
self.iter = range(n - 1) # iter object
|
||||||
if weight:
|
if weight:
|
||||||
self.w = nn.Parameter(-torch.arange(1., n) / 2, requires_grad=True) # layer weights
|
self.w = nn.Parameter(-torch.arange(1.0, n) / 2, requires_grad=True) # layer weights
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
y = x[0] # no weight
|
y = x[0] # no weight
|
||||||
@@ -44,92 +33,72 @@ class Sum(nn.Module):
|
|||||||
return y
|
return y
|
||||||
|
|
||||||
|
|
||||||
class GhostConv(nn.Module):
|
|
||||||
# Ghost Convolution https://github.com/huawei-noah/ghostnet
|
|
||||||
def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups
|
|
||||||
super(GhostConv, self).__init__()
|
|
||||||
c_ = c2 // 2 # hidden channels
|
|
||||||
self.cv1 = Conv(c1, c_, k, s, None, g, act)
|
|
||||||
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
|
|
||||||
|
|
||||||
def forward(self, x):
|
|
||||||
y = self.cv1(x)
|
|
||||||
return torch.cat([y, self.cv2(y)], 1)
|
|
||||||
|
|
||||||
|
|
||||||
class GhostBottleneck(nn.Module):
|
|
||||||
# Ghost Bottleneck https://github.com/huawei-noah/ghostnet
|
|
||||||
def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
|
|
||||||
super(GhostBottleneck, self).__init__()
|
|
||||||
c_ = c2 // 2
|
|
||||||
self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw
|
|
||||||
DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw
|
|
||||||
GhostConv(c_, c2, 1, 1, act=False)) # pw-linear
|
|
||||||
self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False),
|
|
||||||
Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity()
|
|
||||||
|
|
||||||
def forward(self, x):
|
|
||||||
return self.conv(x) + self.shortcut(x)
|
|
||||||
|
|
||||||
|
|
||||||
class MixConv2d(nn.Module):
|
class MixConv2d(nn.Module):
|
||||||
# Mixed Depthwise Conv https://arxiv.org/abs/1907.09595
|
# Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595
|
||||||
def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
|
def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): # ch_in, ch_out, kernel, stride, ch_strategy
|
||||||
super(MixConv2d, self).__init__()
|
super().__init__()
|
||||||
groups = len(k)
|
n = len(k) # number of convolutions
|
||||||
if equal_ch: # equal c_ per group
|
if equal_ch: # equal c_ per group
|
||||||
i = torch.linspace(0, groups - 1E-6, c2).floor() # c2 indices
|
i = torch.linspace(0, n - 1E-6, c2).floor() # c2 indices
|
||||||
c_ = [(i == g).sum() for g in range(groups)] # intermediate channels
|
c_ = [(i == g).sum() for g in range(n)] # intermediate channels
|
||||||
else: # equal weight.numel() per group
|
else: # equal weight.numel() per group
|
||||||
b = [c2] + [0] * groups
|
b = [c2] + [0] * n
|
||||||
a = np.eye(groups + 1, groups, k=-1)
|
a = np.eye(n + 1, n, k=-1)
|
||||||
a -= np.roll(a, 1, axis=1)
|
a -= np.roll(a, 1, axis=1)
|
||||||
a *= np.array(k) ** 2
|
a *= np.array(k) ** 2
|
||||||
a[0] = 1
|
a[0] = 1
|
||||||
c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b
|
c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b
|
||||||
|
|
||||||
self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)])
|
self.m = nn.ModuleList([
|
||||||
|
nn.Conv2d(c1, int(c_), k, s, k // 2, groups=math.gcd(c1, int(c_)), bias=False) for k, c_ in zip(k, c_)])
|
||||||
self.bn = nn.BatchNorm2d(c2)
|
self.bn = nn.BatchNorm2d(c2)
|
||||||
self.act = nn.LeakyReLU(0.1, inplace=True)
|
self.act = nn.SiLU()
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))
|
return self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))
|
||||||
|
|
||||||
|
|
||||||
class Ensemble(nn.ModuleList):
|
class Ensemble(nn.ModuleList):
|
||||||
# Ensemble of models
|
# Ensemble of models
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Ensemble, self).__init__()
|
super().__init__()
|
||||||
|
|
||||||
def forward(self, x, augment=False):
|
def forward(self, x, augment=False, profile=False, visualize=False):
|
||||||
y = []
|
y = [module(x, augment, profile, visualize)[0] for module in self]
|
||||||
for module in self:
|
|
||||||
y.append(module(x, augment)[0])
|
|
||||||
# y = torch.stack(y).max(0)[0] # max ensemble
|
# y = torch.stack(y).max(0)[0] # max ensemble
|
||||||
# y = torch.stack(y).mean(0) # mean ensemble
|
# y = torch.stack(y).mean(0) # mean ensemble
|
||||||
y = torch.cat(y, 1) # nms ensemble
|
y = torch.cat(y, 1) # nms ensemble
|
||||||
return y, None # inference, train output
|
return y, None # inference, train output
|
||||||
|
|
||||||
|
|
||||||
def attempt_load(weights, map_location=None):
|
def attempt_load(weights, device=None, inplace=True, fuse=True):
|
||||||
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
|
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
|
||||||
|
from models.yolo import Detect, Model
|
||||||
|
|
||||||
model = Ensemble()
|
model = Ensemble()
|
||||||
for w in weights if isinstance(weights, list) else [weights]:
|
for w in weights if isinstance(weights, list) else [weights]:
|
||||||
attempt_download(w)
|
ckpt = torch.load(attempt_download(w), map_location='cpu') # load
|
||||||
ckpt = torch.load(w, map_location=map_location) # load
|
ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model
|
||||||
model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model
|
model.append(ckpt.fuse().eval() if fuse else ckpt.eval()) # fused or un-fused model in eval mode
|
||||||
|
|
||||||
# Compatibility updates
|
# Compatibility updates
|
||||||
for m in model.modules():
|
for m in model.modules():
|
||||||
if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]:
|
t = type(m)
|
||||||
m.inplace = True # pytorch 1.7.0 compatibility
|
if t in (nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model):
|
||||||
elif type(m) is Conv:
|
m.inplace = inplace # torch 1.7.0 compatibility
|
||||||
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
|
if t is Detect and not isinstance(m.anchor_grid, list):
|
||||||
|
delattr(m, 'anchor_grid')
|
||||||
|
setattr(m, 'anchor_grid', [torch.zeros(1)] * m.nl)
|
||||||
|
elif t is Conv:
|
||||||
|
m._non_persistent_buffers_set = set() # torch 1.6.0 compatibility
|
||||||
|
elif t is nn.Upsample and not hasattr(m, 'recompute_scale_factor'):
|
||||||
|
m.recompute_scale_factor = None # torch 1.11.0 compatibility
|
||||||
|
|
||||||
if len(model) == 1:
|
if len(model) == 1:
|
||||||
return model[-1] # return model
|
return model[-1] # return model
|
||||||
else:
|
print(f'Ensemble created with {weights}\n')
|
||||||
print('Ensemble created with %s\n' % weights)
|
for k in 'names', 'nc', 'yaml':
|
||||||
for k in ['names', 'stride']:
|
setattr(model, k, getattr(model[0], k))
|
||||||
setattr(model, k, getattr(model[-1], k))
|
model.stride = model[torch.argmax(torch.tensor([m.stride.max() for m in model])).int()].stride # max stride
|
||||||
return model # return ensemble
|
assert all(model[0].nc == m.nc for m in model), f'Models have different class counts: {[m.nc for m in model]}'
|
||||||
|
return model # return ensemble
|
||||||
+32
-31
@@ -1,58 +1,59 @@
|
|||||||
# Default YOLOv5 anchors for COCO data
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
# Default anchors for COCO data
|
||||||
|
|
||||||
|
|
||||||
# P5 -------------------------------------------------------------------------------------------------------------------
|
# P5 -------------------------------------------------------------------------------------------------------------------
|
||||||
# P5-640:
|
# P5-640:
|
||||||
anchors_p5_640:
|
anchors_p5_640:
|
||||||
- [ 10,13, 16,30, 33,23 ] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [ 30,61, 62,45, 59,119 ] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [ 116,90, 156,198, 373,326 ] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
|
||||||
# P6 -------------------------------------------------------------------------------------------------------------------
|
# P6 -------------------------------------------------------------------------------------------------------------------
|
||||||
# P6-640: thr=0.25: 0.9964 BPR, 5.54 anchors past thr, n=12, img_size=640, metric_all=0.281/0.716-mean/best, past_thr=0.469-mean: 9,11, 21,19, 17,41, 43,32, 39,70, 86,64, 65,131, 134,130, 120,265, 282,180, 247,354, 512,387
|
# P6-640: thr=0.25: 0.9964 BPR, 5.54 anchors past thr, n=12, img_size=640, metric_all=0.281/0.716-mean/best, past_thr=0.469-mean: 9,11, 21,19, 17,41, 43,32, 39,70, 86,64, 65,131, 134,130, 120,265, 282,180, 247,354, 512,387
|
||||||
anchors_p6_640:
|
anchors_p6_640:
|
||||||
- [ 9,11, 21,19, 17,41 ] # P3/8
|
- [9,11, 21,19, 17,41] # P3/8
|
||||||
- [ 43,32, 39,70, 86,64 ] # P4/16
|
- [43,32, 39,70, 86,64] # P4/16
|
||||||
- [ 65,131, 134,130, 120,265 ] # P5/32
|
- [65,131, 134,130, 120,265] # P5/32
|
||||||
- [ 282,180, 247,354, 512,387 ] # P6/64
|
- [282,180, 247,354, 512,387] # P6/64
|
||||||
|
|
||||||
# P6-1280: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1280, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 19,27, 44,40, 38,94, 96,68, 86,152, 180,137, 140,301, 303,264, 238,542, 436,615, 739,380, 925,792
|
# P6-1280: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1280, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 19,27, 44,40, 38,94, 96,68, 86,152, 180,137, 140,301, 303,264, 238,542, 436,615, 739,380, 925,792
|
||||||
anchors_p6_1280:
|
anchors_p6_1280:
|
||||||
- [ 19,27, 44,40, 38,94 ] # P3/8
|
- [19,27, 44,40, 38,94] # P3/8
|
||||||
- [ 96,68, 86,152, 180,137 ] # P4/16
|
- [96,68, 86,152, 180,137] # P4/16
|
||||||
- [ 140,301, 303,264, 238,542 ] # P5/32
|
- [140,301, 303,264, 238,542] # P5/32
|
||||||
- [ 436,615, 739,380, 925,792 ] # P6/64
|
- [436,615, 739,380, 925,792] # P6/64
|
||||||
|
|
||||||
# P6-1920: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1920, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 28,41, 67,59, 57,141, 144,103, 129,227, 270,205, 209,452, 455,396, 358,812, 653,922, 1109,570, 1387,1187
|
# P6-1920: thr=0.25: 0.9950 BPR, 5.55 anchors past thr, n=12, img_size=1920, metric_all=0.281/0.714-mean/best, past_thr=0.468-mean: 28,41, 67,59, 57,141, 144,103, 129,227, 270,205, 209,452, 455,396, 358,812, 653,922, 1109,570, 1387,1187
|
||||||
anchors_p6_1920:
|
anchors_p6_1920:
|
||||||
- [ 28,41, 67,59, 57,141 ] # P3/8
|
- [28,41, 67,59, 57,141] # P3/8
|
||||||
- [ 144,103, 129,227, 270,205 ] # P4/16
|
- [144,103, 129,227, 270,205] # P4/16
|
||||||
- [ 209,452, 455,396, 358,812 ] # P5/32
|
- [209,452, 455,396, 358,812] # P5/32
|
||||||
- [ 653,922, 1109,570, 1387,1187 ] # P6/64
|
- [653,922, 1109,570, 1387,1187] # P6/64
|
||||||
|
|
||||||
|
|
||||||
# P7 -------------------------------------------------------------------------------------------------------------------
|
# P7 -------------------------------------------------------------------------------------------------------------------
|
||||||
# P7-640: thr=0.25: 0.9962 BPR, 6.76 anchors past thr, n=15, img_size=640, metric_all=0.275/0.733-mean/best, past_thr=0.466-mean: 11,11, 13,30, 29,20, 30,46, 61,38, 39,92, 78,80, 146,66, 79,163, 149,150, 321,143, 157,303, 257,402, 359,290, 524,372
|
# P7-640: thr=0.25: 0.9962 BPR, 6.76 anchors past thr, n=15, img_size=640, metric_all=0.275/0.733-mean/best, past_thr=0.466-mean: 11,11, 13,30, 29,20, 30,46, 61,38, 39,92, 78,80, 146,66, 79,163, 149,150, 321,143, 157,303, 257,402, 359,290, 524,372
|
||||||
anchors_p7_640:
|
anchors_p7_640:
|
||||||
- [ 11,11, 13,30, 29,20 ] # P3/8
|
- [11,11, 13,30, 29,20] # P3/8
|
||||||
- [ 30,46, 61,38, 39,92 ] # P4/16
|
- [30,46, 61,38, 39,92] # P4/16
|
||||||
- [ 78,80, 146,66, 79,163 ] # P5/32
|
- [78,80, 146,66, 79,163] # P5/32
|
||||||
- [ 149,150, 321,143, 157,303 ] # P6/64
|
- [149,150, 321,143, 157,303] # P6/64
|
||||||
- [ 257,402, 359,290, 524,372 ] # P7/128
|
- [257,402, 359,290, 524,372] # P7/128
|
||||||
|
|
||||||
# P7-1280: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1280, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 19,22, 54,36, 32,77, 70,83, 138,71, 75,173, 165,159, 148,334, 375,151, 334,317, 251,626, 499,474, 750,326, 534,814, 1079,818
|
# P7-1280: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1280, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 19,22, 54,36, 32,77, 70,83, 138,71, 75,173, 165,159, 148,334, 375,151, 334,317, 251,626, 499,474, 750,326, 534,814, 1079,818
|
||||||
anchors_p7_1280:
|
anchors_p7_1280:
|
||||||
- [ 19,22, 54,36, 32,77 ] # P3/8
|
- [19,22, 54,36, 32,77] # P3/8
|
||||||
- [ 70,83, 138,71, 75,173 ] # P4/16
|
- [70,83, 138,71, 75,173] # P4/16
|
||||||
- [ 165,159, 148,334, 375,151 ] # P5/32
|
- [165,159, 148,334, 375,151] # P5/32
|
||||||
- [ 334,317, 251,626, 499,474 ] # P6/64
|
- [334,317, 251,626, 499,474] # P6/64
|
||||||
- [ 750,326, 534,814, 1079,818 ] # P7/128
|
- [750,326, 534,814, 1079,818] # P7/128
|
||||||
|
|
||||||
# P7-1920: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1920, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 29,34, 81,55, 47,115, 105,124, 207,107, 113,259, 247,238, 222,500, 563,227, 501,476, 376,939, 749,711, 1126,489, 801,1222, 1618,1227
|
# P7-1920: thr=0.25: 0.9968 BPR, 6.71 anchors past thr, n=15, img_size=1920, metric_all=0.273/0.732-mean/best, past_thr=0.463-mean: 29,34, 81,55, 47,115, 105,124, 207,107, 113,259, 247,238, 222,500, 563,227, 501,476, 376,939, 749,711, 1126,489, 801,1222, 1618,1227
|
||||||
anchors_p7_1920:
|
anchors_p7_1920:
|
||||||
- [ 29,34, 81,55, 47,115 ] # P3/8
|
- [29,34, 81,55, 47,115] # P3/8
|
||||||
- [ 105,124, 207,107, 113,259 ] # P4/16
|
- [105,124, 207,107, 113,259] # P4/16
|
||||||
- [ 247,238, 222,500, 563,227 ] # P5/32
|
- [247,238, 222,500, 563,227] # P5/32
|
||||||
- [ 501,476, 376,939, 749,711 ] # P6/64
|
- [501,476, 376,939, 749,711] # P6/64
|
||||||
- [ 1126,489, 801,1222, 1618,1227 ] # P7/128
|
- [1126,489, 801,1222, 1618,1227] # P7/128
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,14, 23,27, 37,58] # P4/16
|
- [10,14, 23,27, 37,58] # P4/16
|
||||||
- [81,82, 135,169, 344,319] # P5/32
|
- [81,82, 135,169, 344,319] # P5/32
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
@@ -28,7 +28,7 @@ backbone:
|
|||||||
# YOLOv3 head
|
# YOLOv3 head
|
||||||
head:
|
head:
|
||||||
[[-1, 1, Bottleneck, [1024, False]],
|
[[-1, 1, Bottleneck, [1024, False]],
|
||||||
[-1, 1, Conv, [512, [1, 1]]],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, Conv, [1024, 3, 1]],
|
[-1, 1, Conv, [1024, 3, 1]],
|
||||||
[-1, 1, Conv, [512, 1, 1]],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large)
|
[-1, 1, Conv, [1024, 3, 1]], # 15 (P5/32-large)
|
||||||
|
|||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 1.0 # model depth multiple
|
||||||
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 6, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 3, C3, [1024]],
|
||||||
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 BiFPN head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14, 6], 1, Concat, [1]], # cat P4 <--- BiFPN change
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
@@ -1,42 +1,42 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [116,90, 156,198, 373,326] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[-1, 3, Bottleneck, [128]],
|
[-1, 3, C3, [128]],
|
||||||
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[-1, 9, BottleneckCSP, [256]],
|
[-1, 6, C3, [256]],
|
||||||
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[-1, 9, BottleneckCSP, [512]],
|
[-1, 9, C3, [512]],
|
||||||
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
[-1, 3, C3, [1024]],
|
||||||
[-1, 6, BottleneckCSP, [1024]], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 FPN head
|
# YOLOv5 v6.0 FPN head
|
||||||
head:
|
head:
|
||||||
[[-1, 3, BottleneckCSP, [1024, False]], # 10 (P5/32-large)
|
[[-1, 3, C3, [1024, False]], # 10 (P5/32-large)
|
||||||
|
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[-1, 1, Conv, [512, 1, 1]],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 3, BottleneckCSP, [512, False]], # 14 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 14 (P4/16-medium)
|
||||||
|
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[-1, 1, Conv, [256, 1, 1]],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[-1, 3, BottleneckCSP, [256, False]], # 18 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 18 (P3/8-small)
|
||||||
|
|
||||||
[[18, 14, 10], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
[[18, 14, 10], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
]
|
]
|
||||||
@@ -1,54 +1,54 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
anchors: 3 # AutoAnchor evolves 3 anchors per P output layer
|
||||||
|
|
||||||
# anchors
|
# YOLOv5 v6.0 backbone
|
||||||
anchors: 3
|
|
||||||
|
|
||||||
# YOLOv5 backbone
|
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[ -1, 1, SPP, [ 1024, [ 5, 9, 13 ] ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head with (P2, P3, P4, P5) outputs
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 13
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 17 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 128, 1, 1 ] ],
|
[-1, 1, Conv, [128, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 2 ], 1, Concat, [ 1 ] ], # cat backbone P2
|
[[-1, 2], 1, Concat, [1]], # cat backbone P2
|
||||||
[ -1, 1, C3, [ 128, False ] ], # 21 (P2/4-xsmall)
|
[-1, 1, C3, [128, False]], # 21 (P2/4-xsmall)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ],
|
[-1, 1, Conv, [128, 3, 2]],
|
||||||
[ [ -1, 18 ], 1, Concat, [ 1 ] ], # cat head P3
|
[[-1, 18], 1, Concat, [1]], # cat head P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 24 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 24 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 27 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 27 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 10 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 30 (P5/32-large)
|
[-1, 3, C3, [1024, False]], # 30 (P5/32-large)
|
||||||
|
|
||||||
[ [ 24, 27, 30 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5)
|
[[21, 24, 27, 30], 1, Detect, [nc, anchors]], # Detect(P2, P3, P4, P5)
|
||||||
]
|
]
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
anchors: 3 # AutoAnchor evolves 3 anchors per P output layer
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[ [ -1, 1, Conv, [ 64, 6, 2, 2 ] ], # 0-P1/2
|
||||||
|
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
||||||
|
[ -1, 3, C3, [ 128 ] ],
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
||||||
|
[ -1, 6, C3, [ 256 ] ],
|
||||||
|
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
||||||
|
[ -1, 9, C3, [ 512 ] ],
|
||||||
|
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 7-P5/32
|
||||||
|
[ -1, 3, C3, [ 1024 ] ],
|
||||||
|
[ -1, 1, SPPF, [ 1024, 5 ] ], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 head with (P3, P4) outputs
|
||||||
|
head:
|
||||||
|
[ [ -1, 1, Conv, [ 512, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 13
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
||||||
|
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
||||||
|
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
||||||
|
[ -1, 3, C3, [ 256, False ] ], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
||||||
|
[ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P4
|
||||||
|
[ -1, 3, C3, [ 512, False ] ], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[ [ 17, 20 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4)
|
||||||
|
]
|
||||||
@@ -1,56 +1,56 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
anchors: 3 # AutoAnchor evolves 3 anchors per P output layer
|
||||||
|
|
||||||
# anchors
|
# YOLOv5 v6.0 backbone
|
||||||
anchors: 3
|
|
||||||
|
|
||||||
# YOLOv5 backbone
|
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
[ -1, 3, C3, [ 768 ] ],
|
[-1, 3, C3, [768]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 11
|
[-1, 1, SPPF, [1024, 5]], # 11
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head with (P3, P4, P5, P6) outputs
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
[[-1, 1, Conv, [768, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 15
|
[-1, 3, C3, [768, False]], # 15
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 19
|
[-1, 3, C3, [512, False]], # 19
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 23 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 20], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 26 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 16], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
[-1, 3, C3, [768, False]], # 29 (P5/32-large)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
[[-1, 12], 1, Concat, [1]], # cat head P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 32 (P5/64-xlarge)
|
[-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
[[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6)
|
||||||
]
|
]
|
||||||
@@ -1,67 +1,67 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
anchors: 3 # AutoAnchor evolves 3 anchors per P output layer
|
||||||
|
|
||||||
# anchors
|
# YOLOv5 v6.0 backbone
|
||||||
anchors: 3
|
|
||||||
|
|
||||||
# YOLOv5 backbone
|
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
[ -1, 3, C3, [ 768 ] ],
|
[-1, 3, C3, [768]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
[ -1, 3, C3, [ 1024 ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 1, Conv, [ 1280, 3, 2 ] ], # 11-P7/128
|
[-1, 1, Conv, [1280, 3, 2]], # 11-P7/128
|
||||||
[ -1, 1, SPP, [ 1280, [ 3, 5 ] ] ],
|
[-1, 3, C3, [1280]],
|
||||||
[ -1, 3, C3, [ 1280, False ] ], # 13
|
[-1, 1, SPPF, [1280, 5]], # 13
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head with (P3, P4, P5, P6, P7) outputs
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 1024, 1, 1 ] ],
|
[[-1, 1, Conv, [1024, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 10 ], 1, Concat, [ 1 ] ], # cat backbone P6
|
[[-1, 10], 1, Concat, [1]], # cat backbone P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 17
|
[-1, 3, C3, [1024, False]], # 17
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 1, 1 ] ],
|
[-1, 1, Conv, [768, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 21
|
[-1, 3, C3, [768, False]], # 21
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 25
|
[-1, 3, C3, [512, False]], # 25
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 29 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 29 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 26 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 26], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 32 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 32 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 22 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 22], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 35 (P5/32-large)
|
[-1, 3, C3, [768, False]], # 35 (P5/32-large)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
[ [ -1, 18 ], 1, Concat, [ 1 ] ], # cat head P6
|
[[-1, 18], 1, Concat, [1]], # cat head P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 38 (P6/64-xlarge)
|
[-1, 3, C3, [1024, False]], # 38 (P6/64-xlarge)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ],
|
[-1, 1, Conv, [1024, 3, 2]],
|
||||||
[ [ -1, 14 ], 1, Concat, [ 1 ] ], # cat head P7
|
[[-1, 14], 1, Concat, [1]], # cat head P7
|
||||||
[ -1, 3, C3, [ 1280, False ] ], # 41 (P7/128-xxlarge)
|
[-1, 3, C3, [1280, False]], # 41 (P7/128-xxlarge)
|
||||||
|
|
||||||
[ [ 29, 32, 35, 38, 41 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6, P7)
|
[[29, 32, 35, 38, 41], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6, P7)
|
||||||
]
|
]
|
||||||
@@ -1,48 +1,48 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [116,90, 156,198, 373,326] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[-1, 3, BottleneckCSP, [128]],
|
[-1, 3, C3, [128]],
|
||||||
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[-1, 9, BottleneckCSP, [256]],
|
[-1, 6, C3, [256]],
|
||||||
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[-1, 9, BottleneckCSP, [512]],
|
[-1, 9, C3, [512]],
|
||||||
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
[-1, 3, C3, [1024]],
|
||||||
[-1, 3, BottleneckCSP, [1024, False]], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 PANet head
|
# YOLOv5 v6.0 PANet head
|
||||||
head:
|
head:
|
||||||
[[-1, 1, Conv, [512, 1, 1]],
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[-1, 3, BottleneckCSP, [512, False]], # 13
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
[-1, 1, Conv, [256, 1, 1]],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[-1, 3, BottleneckCSP, [256, False]], # 17 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
[-1, 1, Conv, [256, 3, 2]],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[[-1, 14], 1, Concat, [1]], # cat head P4
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
[-1, 3, BottleneckCSP, [512, False]], # 20 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
[-1, 1, Conv, [512, 3, 2]],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[[-1, 10], 1, Concat, [1]], # cat head P5
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
[-1, 3, BottleneckCSP, [1024, False]], # 23 (P5/32-large)
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
]
|
]
|
||||||
+43
-43
@@ -1,60 +1,60 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [ 19,27, 44,40, 38,94 ] # P3/8
|
- [19,27, 44,40, 38,94] # P3/8
|
||||||
- [ 96,68, 86,152, 180,137 ] # P4/16
|
- [96,68, 86,152, 180,137] # P4/16
|
||||||
- [ 140,301, 303,264, 238,542 ] # P5/32
|
- [140,301, 303,264, 238,542] # P5/32
|
||||||
- [ 436,615, 739,380, 925,792 ] # P6/64
|
- [436,615, 739,380, 925,792] # P6/64
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
[ -1, 3, C3, [ 768 ] ],
|
[-1, 3, C3, [768]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 11
|
[-1, 1, SPPF, [1024, 5]], # 11
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
[[-1, 1, Conv, [768, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 15
|
[-1, 3, C3, [768, False]], # 15
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 19
|
[-1, 3, C3, [512, False]], # 19
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 23 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 20], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 26 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 16], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
[-1, 3, C3, [768, False]], # 29 (P5/32-large)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
[[-1, 12], 1, Concat, [1]], # cat head P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
[-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
[[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6)
|
||||||
]
|
]
|
||||||
+43
-43
@@ -1,60 +1,60 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 0.67 # model depth multiple
|
depth_multiple: 0.67 # model depth multiple
|
||||||
width_multiple: 0.75 # layer channel multiple
|
width_multiple: 0.75 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [ 19,27, 44,40, 38,94 ] # P3/8
|
- [19,27, 44,40, 38,94] # P3/8
|
||||||
- [ 96,68, 86,152, 180,137 ] # P4/16
|
- [96,68, 86,152, 180,137] # P4/16
|
||||||
- [ 140,301, 303,264, 238,542 ] # P5/32
|
- [140,301, 303,264, 238,542] # P5/32
|
||||||
- [ 436,615, 739,380, 925,792 ] # P6/64
|
- [436,615, 739,380, 925,792] # P6/64
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
[ -1, 3, C3, [ 768 ] ],
|
[-1, 3, C3, [768]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 11
|
[-1, 1, SPPF, [1024, 5]], # 11
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
[[-1, 1, Conv, [768, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 15
|
[-1, 3, C3, [768, False]], # 15
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 19
|
[-1, 3, C3, [512, False]], # 19
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 23 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 20], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 26 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 16], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
[-1, 3, C3, [768, False]], # 29 (P5/32-large)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
[[-1, 12], 1, Concat, [1]], # cat head P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
[-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
[[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6)
|
||||||
]
|
]
|
||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.25 # layer channel multiple
|
||||||
|
anchors:
|
||||||
|
- [19,27, 44,40, 38,94] # P3/8
|
||||||
|
- [96,68, 86,152, 180,137] # P4/16
|
||||||
|
- [140,301, 303,264, 238,542] # P5/32
|
||||||
|
- [436,615, 739,380, 925,792] # P6/64
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 6, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 3, C3, [768]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
|
[-1, 3, C3, [1024]],
|
||||||
|
[-1, 1, SPPF, [1024, 5]], # 11
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [768, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
|
[-1, 3, C3, [768, False]], # 15
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 19
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 23 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 20], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 26 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 16], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [768, False]], # 29 (P5/32-large)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
|
[[-1, 12], 1, Concat, [1]], # cat head P6
|
||||||
|
[-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
|
[[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6)
|
||||||
|
]
|
||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
|
[-1, 1, GhostConv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3Ghost, [128]],
|
||||||
|
[-1, 1, GhostConv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 6, C3Ghost, [256]],
|
||||||
|
[-1, 1, GhostConv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3Ghost, [512]],
|
||||||
|
[-1, 1, GhostConv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 3, C3Ghost, [1024]],
|
||||||
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, GhostConv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3Ghost, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, GhostConv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3Ghost, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, GhostConv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3Ghost, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, GhostConv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3Ghost, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 6, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 3, C3TR, [1024]], # 9 <--- C3TR() Transformer module
|
||||||
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
+43
-43
@@ -1,60 +1,60 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 0.33 # model depth multiple
|
depth_multiple: 0.33 # model depth multiple
|
||||||
width_multiple: 0.50 # layer channel multiple
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [ 19,27, 44,40, 38,94 ] # P3/8
|
- [19,27, 44,40, 38,94] # P3/8
|
||||||
- [ 96,68, 86,152, 180,137 ] # P4/16
|
- [96,68, 86,152, 180,137] # P4/16
|
||||||
- [ 140,301, 303,264, 238,542 ] # P5/32
|
- [140,301, 303,264, 238,542] # P5/32
|
||||||
- [ 436,615, 739,380, 925,792 ] # P6/64
|
- [436,615, 739,380, 925,792] # P6/64
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
[ -1, 3, C3, [ 768 ] ],
|
[-1, 3, C3, [768]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 11
|
[-1, 1, SPPF, [1024, 5]], # 11
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
[[-1, 1, Conv, [768, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 15
|
[-1, 3, C3, [768, False]], # 15
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 19
|
[-1, 3, C3, [512, False]], # 19
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 23 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 20], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 26 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 16], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
[-1, 3, C3, [768, False]], # 29 (P5/32-large)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
[[-1, 12], 1, Concat, [1]], # cat head P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
[-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
[[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6)
|
||||||
]
|
]
|
||||||
+43
-43
@@ -1,60 +1,60 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.33 # model depth multiple
|
depth_multiple: 1.33 # model depth multiple
|
||||||
width_multiple: 1.25 # layer channel multiple
|
width_multiple: 1.25 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [ 19,27, 44,40, 38,94 ] # P3/8
|
- [19,27, 44,40, 38,94] # P3/8
|
||||||
- [ 96,68, 86,152, 180,137 ] # P4/16
|
- [96,68, 86,152, 180,137] # P4/16
|
||||||
- [ 140,301, 303,264, 238,542 ] # P5/32
|
- [140,301, 303,264, 238,542] # P5/32
|
||||||
- [ 436,615, 739,380, 925,792 ] # P6/64
|
- [436,615, 739,380, 925,792] # P6/64
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[ [ -1, 1, Focus, [ 64, 3 ] ], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[ -1, 1, Conv, [ 128, 3, 2 ] ], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[ -1, 3, C3, [ 128 ] ],
|
[-1, 3, C3, [128]],
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[ -1, 9, C3, [ 256 ] ],
|
[-1, 6, C3, [256]],
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[ -1, 9, C3, [ 512 ] ],
|
[-1, 9, C3, [512]],
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ], # 7-P5/32
|
[-1, 1, Conv, [768, 3, 2]], # 7-P5/32
|
||||||
[ -1, 3, C3, [ 768 ] ],
|
[-1, 3, C3, [768]],
|
||||||
[ -1, 1, Conv, [ 1024, 3, 2 ] ], # 9-P6/64
|
[-1, 1, Conv, [1024, 3, 2]], # 9-P6/64
|
||||||
[ -1, 1, SPP, [ 1024, [ 3, 5, 7 ] ] ],
|
[-1, 3, C3, [1024]],
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 11
|
[-1, 1, SPPF, [1024, 5]], # 11
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[ [ -1, 1, Conv, [ 768, 1, 1 ] ],
|
[[-1, 1, Conv, [768, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 8 ], 1, Concat, [ 1 ] ], # cat backbone P5
|
[[-1, 8], 1, Concat, [1]], # cat backbone P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 15
|
[-1, 3, C3, [768, False]], # 15
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 1, 1 ] ],
|
[-1, 1, Conv, [512, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 6 ], 1, Concat, [ 1 ] ], # cat backbone P4
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 19
|
[-1, 3, C3, [512, False]], # 19
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 1, 1 ] ],
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
[ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
[ [ -1, 4 ], 1, Concat, [ 1 ] ], # cat backbone P3
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
[ -1, 3, C3, [ 256, False ] ], # 23 (P3/8-small)
|
[-1, 3, C3, [256, False]], # 23 (P3/8-small)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 256, 3, 2 ] ],
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
[ [ -1, 20 ], 1, Concat, [ 1 ] ], # cat head P4
|
[[-1, 20], 1, Concat, [1]], # cat head P4
|
||||||
[ -1, 3, C3, [ 512, False ] ], # 26 (P4/16-medium)
|
[-1, 3, C3, [512, False]], # 26 (P4/16-medium)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 512, 3, 2 ] ],
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
[ [ -1, 16 ], 1, Concat, [ 1 ] ], # cat head P5
|
[[-1, 16], 1, Concat, [1]], # cat head P5
|
||||||
[ -1, 3, C3, [ 768, False ] ], # 29 (P5/32-large)
|
[-1, 3, C3, [768, False]], # 29 (P5/32-large)
|
||||||
|
|
||||||
[ -1, 1, Conv, [ 768, 3, 2 ] ],
|
[-1, 1, Conv, [768, 3, 2]],
|
||||||
[ [ -1, 12 ], 1, Concat, [ 1 ] ], # cat head P6
|
[[-1, 12], 1, Concat, [1]], # cat head P6
|
||||||
[ -1, 3, C3, [ 1024, False ] ], # 32 (P6/64-xlarge)
|
[-1, 3, C3, [1024, False]], # 32 (P6/64-xlarge)
|
||||||
|
|
||||||
[ [ 23, 26, 29, 32 ], 1, Detect, [ nc, anchors ] ], # Detect(P3, P4, P5, P6)
|
[[23, 26, 29, 32], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5, P6)
|
||||||
]
|
]
|
||||||
Executable
+574
@@ -0,0 +1,574 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
"""
|
||||||
|
TensorFlow, Keras and TFLite versions of YOLOv5
|
||||||
|
Authored by https://github.com/zldrobit in PR https://github.com/ultralytics/yolov5/pull/1127
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$ python models/tf.py --weights yolov5s.pt
|
||||||
|
|
||||||
|
Export:
|
||||||
|
$ python path/to/export.py --weights yolov5s.pt --include saved_model pb tflite tfjs
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from copy import deepcopy
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
FILE = Path(__file__).resolve()
|
||||||
|
ROOT = FILE.parents[1] # YOLOv5 root directory
|
||||||
|
if str(ROOT) not in sys.path:
|
||||||
|
sys.path.append(str(ROOT)) # add ROOT to PATH
|
||||||
|
# ROOT = ROOT.relative_to(Path.cwd()) # relative
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import tensorflow as tf
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
from tensorflow import keras
|
||||||
|
|
||||||
|
from models.common import (C3, SPP, SPPF, Bottleneck, BottleneckCSP, C3x, Concat, Conv, CrossConv, DWConv,
|
||||||
|
DWConvTranspose2d, Focus, autopad)
|
||||||
|
from models.experimental import MixConv2d, attempt_load
|
||||||
|
from models.yolo import Detect
|
||||||
|
from utils.activations import SiLU
|
||||||
|
from utils.general import LOGGER, make_divisible, print_args
|
||||||
|
|
||||||
|
|
||||||
|
class TFBN(keras.layers.Layer):
|
||||||
|
# TensorFlow BatchNormalization wrapper
|
||||||
|
def __init__(self, w=None):
|
||||||
|
super().__init__()
|
||||||
|
self.bn = keras.layers.BatchNormalization(
|
||||||
|
beta_initializer=keras.initializers.Constant(w.bias.numpy()),
|
||||||
|
gamma_initializer=keras.initializers.Constant(w.weight.numpy()),
|
||||||
|
moving_mean_initializer=keras.initializers.Constant(w.running_mean.numpy()),
|
||||||
|
moving_variance_initializer=keras.initializers.Constant(w.running_var.numpy()),
|
||||||
|
epsilon=w.eps)
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.bn(inputs)
|
||||||
|
|
||||||
|
|
||||||
|
class TFPad(keras.layers.Layer):
|
||||||
|
# Pad inputs in spatial dimensions 1 and 2
|
||||||
|
def __init__(self, pad):
|
||||||
|
super().__init__()
|
||||||
|
if isinstance(pad, int):
|
||||||
|
self.pad = tf.constant([[0, 0], [pad, pad], [pad, pad], [0, 0]])
|
||||||
|
else: # tuple/list
|
||||||
|
self.pad = tf.constant([[0, 0], [pad[0], pad[0]], [pad[1], pad[1]], [0, 0]])
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return tf.pad(inputs, self.pad, mode='constant', constant_values=0)
|
||||||
|
|
||||||
|
|
||||||
|
class TFConv(keras.layers.Layer):
|
||||||
|
# Standard convolution
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, w=None):
|
||||||
|
# ch_in, ch_out, weights, kernel, stride, padding, groups
|
||||||
|
super().__init__()
|
||||||
|
assert g == 1, "TF v2.2 Conv2D does not support 'groups' argument"
|
||||||
|
# TensorFlow convolution padding is inconsistent with PyTorch (e.g. k=3 s=2 'SAME' padding)
|
||||||
|
# see https://stackoverflow.com/questions/52975843/comparing-conv2d-with-padding-between-tensorflow-and-pytorch
|
||||||
|
conv = keras.layers.Conv2D(
|
||||||
|
filters=c2,
|
||||||
|
kernel_size=k,
|
||||||
|
strides=s,
|
||||||
|
padding='SAME' if s == 1 else 'VALID',
|
||||||
|
use_bias=not hasattr(w, 'bn'),
|
||||||
|
kernel_initializer=keras.initializers.Constant(w.conv.weight.permute(2, 3, 1, 0).numpy()),
|
||||||
|
bias_initializer='zeros' if hasattr(w, 'bn') else keras.initializers.Constant(w.conv.bias.numpy()))
|
||||||
|
self.conv = conv if s == 1 else keras.Sequential([TFPad(autopad(k, p)), conv])
|
||||||
|
self.bn = TFBN(w.bn) if hasattr(w, 'bn') else tf.identity
|
||||||
|
self.act = activations(w.act) if act else tf.identity
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.act(self.bn(self.conv(inputs)))
|
||||||
|
|
||||||
|
|
||||||
|
class TFDWConv(keras.layers.Layer):
|
||||||
|
# Depthwise convolution
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p=None, act=True, w=None):
|
||||||
|
# ch_in, ch_out, weights, kernel, stride, padding, groups
|
||||||
|
super().__init__()
|
||||||
|
assert c2 % c1 == 0, f'TFDWConv() output={c2} must be a multiple of input={c1} channels'
|
||||||
|
conv = keras.layers.DepthwiseConv2D(
|
||||||
|
kernel_size=k,
|
||||||
|
depth_multiplier=c2 // c1,
|
||||||
|
strides=s,
|
||||||
|
padding='SAME' if s == 1 else 'VALID',
|
||||||
|
use_bias=not hasattr(w, 'bn'),
|
||||||
|
depthwise_initializer=keras.initializers.Constant(w.conv.weight.permute(2, 3, 1, 0).numpy()),
|
||||||
|
bias_initializer='zeros' if hasattr(w, 'bn') else keras.initializers.Constant(w.conv.bias.numpy()))
|
||||||
|
self.conv = conv if s == 1 else keras.Sequential([TFPad(autopad(k, p)), conv])
|
||||||
|
self.bn = TFBN(w.bn) if hasattr(w, 'bn') else tf.identity
|
||||||
|
self.act = activations(w.act) if act else tf.identity
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.act(self.bn(self.conv(inputs)))
|
||||||
|
|
||||||
|
|
||||||
|
class TFDWConvTranspose2d(keras.layers.Layer):
|
||||||
|
# Depthwise ConvTranspose2d
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p1=0, p2=0, w=None):
|
||||||
|
# ch_in, ch_out, weights, kernel, stride, padding, groups
|
||||||
|
super().__init__()
|
||||||
|
assert c1 == c2, f'TFDWConv() output={c2} must be equal to input={c1} channels'
|
||||||
|
assert k == 4 and p1 == 1, 'TFDWConv() only valid for k=4 and p1=1'
|
||||||
|
weight, bias = w.weight.permute(2, 3, 1, 0).numpy(), w.bias.numpy()
|
||||||
|
self.c1 = c1
|
||||||
|
self.conv = [
|
||||||
|
keras.layers.Conv2DTranspose(filters=1,
|
||||||
|
kernel_size=k,
|
||||||
|
strides=s,
|
||||||
|
padding='VALID',
|
||||||
|
output_padding=p2,
|
||||||
|
use_bias=True,
|
||||||
|
kernel_initializer=keras.initializers.Constant(weight[..., i:i + 1]),
|
||||||
|
bias_initializer=keras.initializers.Constant(bias[i])) for i in range(c1)]
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return tf.concat([m(x) for m, x in zip(self.conv, tf.split(inputs, self.c1, 3))], 3)[:, 1:-1, 1:-1]
|
||||||
|
|
||||||
|
|
||||||
|
class TFFocus(keras.layers.Layer):
|
||||||
|
# Focus wh information into c-space
|
||||||
|
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True, w=None):
|
||||||
|
# ch_in, ch_out, kernel, stride, padding, groups
|
||||||
|
super().__init__()
|
||||||
|
self.conv = TFConv(c1 * 4, c2, k, s, p, g, act, w.conv)
|
||||||
|
|
||||||
|
def call(self, inputs): # x(b,w,h,c) -> y(b,w/2,h/2,4c)
|
||||||
|
# inputs = inputs / 255 # normalize 0-255 to 0-1
|
||||||
|
inputs = [inputs[:, ::2, ::2, :], inputs[:, 1::2, ::2, :], inputs[:, ::2, 1::2, :], inputs[:, 1::2, 1::2, :]]
|
||||||
|
return self.conv(tf.concat(inputs, 3))
|
||||||
|
|
||||||
|
|
||||||
|
class TFBottleneck(keras.layers.Layer):
|
||||||
|
# Standard bottleneck
|
||||||
|
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5, w=None): # ch_in, ch_out, shortcut, groups, expansion
|
||||||
|
super().__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, 1, 1, w=w.cv1)
|
||||||
|
self.cv2 = TFConv(c_, c2, 3, 1, g=g, w=w.cv2)
|
||||||
|
self.add = shortcut and c1 == c2
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return inputs + self.cv2(self.cv1(inputs)) if self.add else self.cv2(self.cv1(inputs))
|
||||||
|
|
||||||
|
|
||||||
|
class TFCrossConv(keras.layers.Layer):
|
||||||
|
# Cross Convolution
|
||||||
|
def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False, w=None):
|
||||||
|
super().__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, (1, k), (1, s), w=w.cv1)
|
||||||
|
self.cv2 = TFConv(c_, c2, (k, 1), (s, 1), g=g, w=w.cv2)
|
||||||
|
self.add = shortcut and c1 == c2
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return inputs + self.cv2(self.cv1(inputs)) if self.add else self.cv2(self.cv1(inputs))
|
||||||
|
|
||||||
|
|
||||||
|
class TFConv2d(keras.layers.Layer):
|
||||||
|
# Substitution for PyTorch nn.Conv2D
|
||||||
|
def __init__(self, c1, c2, k, s=1, g=1, bias=True, w=None):
|
||||||
|
super().__init__()
|
||||||
|
assert g == 1, "TF v2.2 Conv2D does not support 'groups' argument"
|
||||||
|
self.conv = keras.layers.Conv2D(filters=c2,
|
||||||
|
kernel_size=k,
|
||||||
|
strides=s,
|
||||||
|
padding='VALID',
|
||||||
|
use_bias=bias,
|
||||||
|
kernel_initializer=keras.initializers.Constant(
|
||||||
|
w.weight.permute(2, 3, 1, 0).numpy()),
|
||||||
|
bias_initializer=keras.initializers.Constant(w.bias.numpy()) if bias else None)
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.conv(inputs)
|
||||||
|
|
||||||
|
|
||||||
|
class TFBottleneckCSP(keras.layers.Layer):
|
||||||
|
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, w=None):
|
||||||
|
# ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
|
super().__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, 1, 1, w=w.cv1)
|
||||||
|
self.cv2 = TFConv2d(c1, c_, 1, 1, bias=False, w=w.cv2)
|
||||||
|
self.cv3 = TFConv2d(c_, c_, 1, 1, bias=False, w=w.cv3)
|
||||||
|
self.cv4 = TFConv(2 * c_, c2, 1, 1, w=w.cv4)
|
||||||
|
self.bn = TFBN(w.bn)
|
||||||
|
self.act = lambda x: keras.activations.swish(x)
|
||||||
|
self.m = keras.Sequential([TFBottleneck(c_, c_, shortcut, g, e=1.0, w=w.m[j]) for j in range(n)])
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
y1 = self.cv3(self.m(self.cv1(inputs)))
|
||||||
|
y2 = self.cv2(inputs)
|
||||||
|
return self.cv4(self.act(self.bn(tf.concat((y1, y2), axis=3))))
|
||||||
|
|
||||||
|
|
||||||
|
class TFC3(keras.layers.Layer):
|
||||||
|
# CSP Bottleneck with 3 convolutions
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, w=None):
|
||||||
|
# ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
|
super().__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, 1, 1, w=w.cv1)
|
||||||
|
self.cv2 = TFConv(c1, c_, 1, 1, w=w.cv2)
|
||||||
|
self.cv3 = TFConv(2 * c_, c2, 1, 1, w=w.cv3)
|
||||||
|
self.m = keras.Sequential([TFBottleneck(c_, c_, shortcut, g, e=1.0, w=w.m[j]) for j in range(n)])
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.cv3(tf.concat((self.m(self.cv1(inputs)), self.cv2(inputs)), axis=3))
|
||||||
|
|
||||||
|
|
||||||
|
class TFC3x(keras.layers.Layer):
|
||||||
|
# 3 module with cross-convolutions
|
||||||
|
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5, w=None):
|
||||||
|
# ch_in, ch_out, number, shortcut, groups, expansion
|
||||||
|
super().__init__()
|
||||||
|
c_ = int(c2 * e) # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, 1, 1, w=w.cv1)
|
||||||
|
self.cv2 = TFConv(c1, c_, 1, 1, w=w.cv2)
|
||||||
|
self.cv3 = TFConv(2 * c_, c2, 1, 1, w=w.cv3)
|
||||||
|
self.m = keras.Sequential([
|
||||||
|
TFCrossConv(c_, c_, k=3, s=1, g=g, e=1.0, shortcut=shortcut, w=w.m[j]) for j in range(n)])
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.cv3(tf.concat((self.m(self.cv1(inputs)), self.cv2(inputs)), axis=3))
|
||||||
|
|
||||||
|
|
||||||
|
class TFSPP(keras.layers.Layer):
|
||||||
|
# Spatial pyramid pooling layer used in YOLOv3-SPP
|
||||||
|
def __init__(self, c1, c2, k=(5, 9, 13), w=None):
|
||||||
|
super().__init__()
|
||||||
|
c_ = c1 // 2 # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, 1, 1, w=w.cv1)
|
||||||
|
self.cv2 = TFConv(c_ * (len(k) + 1), c2, 1, 1, w=w.cv2)
|
||||||
|
self.m = [keras.layers.MaxPool2D(pool_size=x, strides=1, padding='SAME') for x in k]
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
x = self.cv1(inputs)
|
||||||
|
return self.cv2(tf.concat([x] + [m(x) for m in self.m], 3))
|
||||||
|
|
||||||
|
|
||||||
|
class TFSPPF(keras.layers.Layer):
|
||||||
|
# Spatial pyramid pooling-Fast layer
|
||||||
|
def __init__(self, c1, c2, k=5, w=None):
|
||||||
|
super().__init__()
|
||||||
|
c_ = c1 // 2 # hidden channels
|
||||||
|
self.cv1 = TFConv(c1, c_, 1, 1, w=w.cv1)
|
||||||
|
self.cv2 = TFConv(c_ * 4, c2, 1, 1, w=w.cv2)
|
||||||
|
self.m = keras.layers.MaxPool2D(pool_size=k, strides=1, padding='SAME')
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
x = self.cv1(inputs)
|
||||||
|
y1 = self.m(x)
|
||||||
|
y2 = self.m(y1)
|
||||||
|
return self.cv2(tf.concat([x, y1, y2, self.m(y2)], 3))
|
||||||
|
|
||||||
|
|
||||||
|
class TFDetect(keras.layers.Layer):
|
||||||
|
# TF YOLOv5 Detect layer
|
||||||
|
def __init__(self, nc=80, anchors=(), ch=(), imgsz=(640, 640), w=None): # detection layer
|
||||||
|
super().__init__()
|
||||||
|
self.stride = tf.convert_to_tensor(w.stride.numpy(), dtype=tf.float32)
|
||||||
|
self.nc = nc # number of classes
|
||||||
|
self.no = nc + 5 # number of outputs per anchor
|
||||||
|
self.nl = len(anchors) # number of detection layers
|
||||||
|
self.na = len(anchors[0]) // 2 # number of anchors
|
||||||
|
self.grid = [tf.zeros(1)] * self.nl # init grid
|
||||||
|
self.anchors = tf.convert_to_tensor(w.anchors.numpy(), dtype=tf.float32)
|
||||||
|
self.anchor_grid = tf.reshape(self.anchors * tf.reshape(self.stride, [self.nl, 1, 1]), [self.nl, 1, -1, 1, 2])
|
||||||
|
self.m = [TFConv2d(x, self.no * self.na, 1, w=w.m[i]) for i, x in enumerate(ch)]
|
||||||
|
self.training = False # set to False after building model
|
||||||
|
self.imgsz = imgsz
|
||||||
|
for i in range(self.nl):
|
||||||
|
ny, nx = self.imgsz[0] // self.stride[i], self.imgsz[1] // self.stride[i]
|
||||||
|
self.grid[i] = self._make_grid(nx, ny)
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
z = [] # inference output
|
||||||
|
x = []
|
||||||
|
for i in range(self.nl):
|
||||||
|
x.append(self.m[i](inputs[i]))
|
||||||
|
# x(bs,20,20,255) to x(bs,3,20,20,85)
|
||||||
|
ny, nx = self.imgsz[0] // self.stride[i], self.imgsz[1] // self.stride[i]
|
||||||
|
x[i] = tf.reshape(x[i], [-1, ny * nx, self.na, self.no])
|
||||||
|
|
||||||
|
if not self.training: # inference
|
||||||
|
y = tf.sigmoid(x[i])
|
||||||
|
grid = tf.transpose(self.grid[i], [0, 2, 1, 3]) - 0.5
|
||||||
|
anchor_grid = tf.transpose(self.anchor_grid[i], [0, 2, 1, 3]) * 4
|
||||||
|
xy = (y[..., 0:2] * 2 + grid) * self.stride[i] # xy
|
||||||
|
wh = y[..., 2:4] ** 2 * anchor_grid
|
||||||
|
# Normalize xywh to 0-1 to reduce calibration error
|
||||||
|
xy /= tf.constant([[self.imgsz[1], self.imgsz[0]]], dtype=tf.float32)
|
||||||
|
wh /= tf.constant([[self.imgsz[1], self.imgsz[0]]], dtype=tf.float32)
|
||||||
|
y = tf.concat([xy, wh, y[..., 4:]], -1)
|
||||||
|
z.append(tf.reshape(y, [-1, self.na * ny * nx, self.no]))
|
||||||
|
|
||||||
|
return tf.transpose(x, [0, 2, 1, 3]) if self.training else (tf.concat(z, 1), x)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _make_grid(nx=20, ny=20):
|
||||||
|
# yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
|
||||||
|
# return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()
|
||||||
|
xv, yv = tf.meshgrid(tf.range(nx), tf.range(ny))
|
||||||
|
return tf.cast(tf.reshape(tf.stack([xv, yv], 2), [1, 1, ny * nx, 2]), dtype=tf.float32)
|
||||||
|
|
||||||
|
|
||||||
|
class TFUpsample(keras.layers.Layer):
|
||||||
|
# TF version of torch.nn.Upsample()
|
||||||
|
def __init__(self, size, scale_factor, mode, w=None): # warning: all arguments needed including 'w'
|
||||||
|
super().__init__()
|
||||||
|
assert scale_factor == 2, "scale_factor must be 2"
|
||||||
|
self.upsample = lambda x: tf.image.resize(x, (x.shape[1] * 2, x.shape[2] * 2), method=mode)
|
||||||
|
# self.upsample = keras.layers.UpSampling2D(size=scale_factor, interpolation=mode)
|
||||||
|
# with default arguments: align_corners=False, half_pixel_centers=False
|
||||||
|
# self.upsample = lambda x: tf.raw_ops.ResizeNearestNeighbor(images=x,
|
||||||
|
# size=(x.shape[1] * 2, x.shape[2] * 2))
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return self.upsample(inputs)
|
||||||
|
|
||||||
|
|
||||||
|
class TFConcat(keras.layers.Layer):
|
||||||
|
# TF version of torch.concat()
|
||||||
|
def __init__(self, dimension=1, w=None):
|
||||||
|
super().__init__()
|
||||||
|
assert dimension == 1, "convert only NCHW to NHWC concat"
|
||||||
|
self.d = 3
|
||||||
|
|
||||||
|
def call(self, inputs):
|
||||||
|
return tf.concat(inputs, self.d)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_model(d, ch, model, imgsz): # model_dict, input_channels(3)
|
||||||
|
LOGGER.info(f"\n{'':>3}{'from':>18}{'n':>3}{'params':>10} {'module':<40}{'arguments':<30}")
|
||||||
|
anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
|
||||||
|
na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors
|
||||||
|
no = na * (nc + 5) # number of outputs = anchors * (classes + 5)
|
||||||
|
|
||||||
|
layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out
|
||||||
|
for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']): # from, number, module, args
|
||||||
|
m_str = m
|
||||||
|
m = eval(m) if isinstance(m, str) else m # eval strings
|
||||||
|
for j, a in enumerate(args):
|
||||||
|
try:
|
||||||
|
args[j] = eval(a) if isinstance(a, str) else a # eval strings
|
||||||
|
except NameError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
n = max(round(n * gd), 1) if n > 1 else n # depth gain
|
||||||
|
if m in [
|
||||||
|
nn.Conv2d, Conv, DWConv, DWConvTranspose2d, Bottleneck, SPP, SPPF, MixConv2d, Focus, CrossConv,
|
||||||
|
BottleneckCSP, C3, C3x]:
|
||||||
|
c1, c2 = ch[f], args[0]
|
||||||
|
c2 = make_divisible(c2 * gw, 8) if c2 != no else c2
|
||||||
|
|
||||||
|
args = [c1, c2, *args[1:]]
|
||||||
|
if m in [BottleneckCSP, C3, C3x]:
|
||||||
|
args.insert(2, n)
|
||||||
|
n = 1
|
||||||
|
elif m is nn.BatchNorm2d:
|
||||||
|
args = [ch[f]]
|
||||||
|
elif m is Concat:
|
||||||
|
c2 = sum(ch[-1 if x == -1 else x + 1] for x in f)
|
||||||
|
elif m is Detect:
|
||||||
|
args.append([ch[x + 1] for x in f])
|
||||||
|
if isinstance(args[1], int): # number of anchors
|
||||||
|
args[1] = [list(range(args[1] * 2))] * len(f)
|
||||||
|
args.append(imgsz)
|
||||||
|
else:
|
||||||
|
c2 = ch[f]
|
||||||
|
|
||||||
|
tf_m = eval('TF' + m_str.replace('nn.', ''))
|
||||||
|
m_ = keras.Sequential([tf_m(*args, w=model.model[i][j]) for j in range(n)]) if n > 1 \
|
||||||
|
else tf_m(*args, w=model.model[i]) # module
|
||||||
|
|
||||||
|
torch_m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module
|
||||||
|
t = str(m)[8:-2].replace('__main__.', '') # module type
|
||||||
|
np = sum(x.numel() for x in torch_m_.parameters()) # number params
|
||||||
|
m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params
|
||||||
|
LOGGER.info(f'{i:>3}{str(f):>18}{str(n):>3}{np:>10} {t:<40}{str(args):<30}') # print
|
||||||
|
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
|
||||||
|
layers.append(m_)
|
||||||
|
ch.append(c2)
|
||||||
|
return keras.Sequential(layers), sorted(save)
|
||||||
|
|
||||||
|
|
||||||
|
class TFModel:
|
||||||
|
# TF YOLOv5 model
|
||||||
|
def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, model=None, imgsz=(640, 640)): # model, channels, classes
|
||||||
|
super().__init__()
|
||||||
|
if isinstance(cfg, dict):
|
||||||
|
self.yaml = cfg # model dict
|
||||||
|
else: # is *.yaml
|
||||||
|
import yaml # for torch hub
|
||||||
|
self.yaml_file = Path(cfg).name
|
||||||
|
with open(cfg) as f:
|
||||||
|
self.yaml = yaml.load(f, Loader=yaml.FullLoader) # model dict
|
||||||
|
|
||||||
|
# Define model
|
||||||
|
if nc and nc != self.yaml['nc']:
|
||||||
|
LOGGER.info(f"Overriding {cfg} nc={self.yaml['nc']} with nc={nc}")
|
||||||
|
self.yaml['nc'] = nc # override yaml value
|
||||||
|
self.model, self.savelist = parse_model(deepcopy(self.yaml), ch=[ch], model=model, imgsz=imgsz)
|
||||||
|
|
||||||
|
def predict(self,
|
||||||
|
inputs,
|
||||||
|
tf_nms=False,
|
||||||
|
agnostic_nms=False,
|
||||||
|
topk_per_class=100,
|
||||||
|
topk_all=100,
|
||||||
|
iou_thres=0.45,
|
||||||
|
conf_thres=0.25):
|
||||||
|
y = [] # outputs
|
||||||
|
x = inputs
|
||||||
|
for m in self.model.layers:
|
||||||
|
if m.f != -1: # if not from previous layer
|
||||||
|
x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
|
||||||
|
|
||||||
|
x = m(x) # run
|
||||||
|
y.append(x if m.i in self.savelist else None) # save output
|
||||||
|
|
||||||
|
# Add TensorFlow NMS
|
||||||
|
if tf_nms:
|
||||||
|
boxes = self._xywh2xyxy(x[0][..., :4])
|
||||||
|
probs = x[0][:, :, 4:5]
|
||||||
|
classes = x[0][:, :, 5:]
|
||||||
|
scores = probs * classes
|
||||||
|
if agnostic_nms:
|
||||||
|
nms = AgnosticNMS()((boxes, classes, scores), topk_all, iou_thres, conf_thres)
|
||||||
|
else:
|
||||||
|
boxes = tf.expand_dims(boxes, 2)
|
||||||
|
nms = tf.image.combined_non_max_suppression(boxes,
|
||||||
|
scores,
|
||||||
|
topk_per_class,
|
||||||
|
topk_all,
|
||||||
|
iou_thres,
|
||||||
|
conf_thres,
|
||||||
|
clip_boxes=False)
|
||||||
|
return nms, x[1]
|
||||||
|
return x[0] # output only first tensor [1,6300,85] = [xywh, conf, class0, class1, ...]
|
||||||
|
# x = x[0][0] # [x(1,6300,85), ...] to x(6300,85)
|
||||||
|
# xywh = x[..., :4] # x(6300,4) boxes
|
||||||
|
# conf = x[..., 4:5] # x(6300,1) confidences
|
||||||
|
# cls = tf.reshape(tf.cast(tf.argmax(x[..., 5:], axis=1), tf.float32), (-1, 1)) # x(6300,1) classes
|
||||||
|
# return tf.concat([conf, cls, xywh], 1)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _xywh2xyxy(xywh):
|
||||||
|
# Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right
|
||||||
|
x, y, w, h = tf.split(xywh, num_or_size_splits=4, axis=-1)
|
||||||
|
return tf.concat([x - w / 2, y - h / 2, x + w / 2, y + h / 2], axis=-1)
|
||||||
|
|
||||||
|
|
||||||
|
class AgnosticNMS(keras.layers.Layer):
|
||||||
|
# TF Agnostic NMS
|
||||||
|
def call(self, input, topk_all, iou_thres, conf_thres):
|
||||||
|
# wrap map_fn to avoid TypeSpec related error https://stackoverflow.com/a/65809989/3036450
|
||||||
|
return tf.map_fn(lambda x: self._nms(x, topk_all, iou_thres, conf_thres),
|
||||||
|
input,
|
||||||
|
fn_output_signature=(tf.float32, tf.float32, tf.float32, tf.int32),
|
||||||
|
name='agnostic_nms')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _nms(x, topk_all=100, iou_thres=0.45, conf_thres=0.25): # agnostic NMS
|
||||||
|
boxes, classes, scores = x
|
||||||
|
class_inds = tf.cast(tf.argmax(classes, axis=-1), tf.float32)
|
||||||
|
scores_inp = tf.reduce_max(scores, -1)
|
||||||
|
selected_inds = tf.image.non_max_suppression(boxes,
|
||||||
|
scores_inp,
|
||||||
|
max_output_size=topk_all,
|
||||||
|
iou_threshold=iou_thres,
|
||||||
|
score_threshold=conf_thres)
|
||||||
|
selected_boxes = tf.gather(boxes, selected_inds)
|
||||||
|
padded_boxes = tf.pad(selected_boxes,
|
||||||
|
paddings=[[0, topk_all - tf.shape(selected_boxes)[0]], [0, 0]],
|
||||||
|
mode="CONSTANT",
|
||||||
|
constant_values=0.0)
|
||||||
|
selected_scores = tf.gather(scores_inp, selected_inds)
|
||||||
|
padded_scores = tf.pad(selected_scores,
|
||||||
|
paddings=[[0, topk_all - tf.shape(selected_boxes)[0]]],
|
||||||
|
mode="CONSTANT",
|
||||||
|
constant_values=-1.0)
|
||||||
|
selected_classes = tf.gather(class_inds, selected_inds)
|
||||||
|
padded_classes = tf.pad(selected_classes,
|
||||||
|
paddings=[[0, topk_all - tf.shape(selected_boxes)[0]]],
|
||||||
|
mode="CONSTANT",
|
||||||
|
constant_values=-1.0)
|
||||||
|
valid_detections = tf.shape(selected_inds)[0]
|
||||||
|
return padded_boxes, padded_scores, padded_classes, valid_detections
|
||||||
|
|
||||||
|
|
||||||
|
def activations(act=nn.SiLU):
|
||||||
|
# Returns TF activation from input PyTorch activation
|
||||||
|
if isinstance(act, nn.LeakyReLU):
|
||||||
|
return lambda x: keras.activations.relu(x, alpha=0.1)
|
||||||
|
elif isinstance(act, nn.Hardswish):
|
||||||
|
return lambda x: x * tf.nn.relu6(x + 3) * 0.166666667
|
||||||
|
elif isinstance(act, (nn.SiLU, SiLU)):
|
||||||
|
return lambda x: keras.activations.swish(x)
|
||||||
|
else:
|
||||||
|
raise Exception(f'no matching TensorFlow activation found for PyTorch activation {act}')
|
||||||
|
|
||||||
|
|
||||||
|
def representative_dataset_gen(dataset, ncalib=100):
|
||||||
|
# Representative dataset generator for use with converter.representative_dataset, returns a generator of np arrays
|
||||||
|
for n, (path, img, im0s, vid_cap, string) in enumerate(dataset):
|
||||||
|
im = np.transpose(img, [1, 2, 0])
|
||||||
|
im = np.expand_dims(im, axis=0).astype(np.float32)
|
||||||
|
im /= 255
|
||||||
|
yield [im]
|
||||||
|
if n >= ncalib:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def run(
|
||||||
|
weights=ROOT / 'yolov5s.pt', # weights path
|
||||||
|
imgsz=(640, 640), # inference size h,w
|
||||||
|
batch_size=1, # batch size
|
||||||
|
dynamic=False, # dynamic batch size
|
||||||
|
):
|
||||||
|
# PyTorch model
|
||||||
|
im = torch.zeros((batch_size, 3, *imgsz)) # BCHW image
|
||||||
|
model = attempt_load(weights, device=torch.device('cpu'), inplace=True, fuse=False)
|
||||||
|
_ = model(im) # inference
|
||||||
|
model.info()
|
||||||
|
|
||||||
|
# TensorFlow model
|
||||||
|
im = tf.zeros((batch_size, *imgsz, 3)) # BHWC image
|
||||||
|
tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
|
||||||
|
_ = tf_model.predict(im) # inference
|
||||||
|
|
||||||
|
# Keras model
|
||||||
|
im = keras.Input(shape=(*imgsz, 3), batch_size=None if dynamic else batch_size)
|
||||||
|
keras_model = keras.Model(inputs=im, outputs=tf_model.predict(im))
|
||||||
|
keras_model.summary()
|
||||||
|
|
||||||
|
LOGGER.info('PyTorch, TensorFlow and Keras models successfully verified.\nUse export.py for TF model export.')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_opt():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='weights path')
|
||||||
|
parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w')
|
||||||
|
parser.add_argument('--batch-size', type=int, default=1, help='batch size')
|
||||||
|
parser.add_argument('--dynamic', action='store_true', help='dynamic batch size')
|
||||||
|
opt = parser.parse_args()
|
||||||
|
opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand
|
||||||
|
print_args(vars(opt))
|
||||||
|
return opt
|
||||||
|
|
||||||
|
|
||||||
|
def main(opt):
|
||||||
|
run(**vars(opt))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
opt = parse_opt()
|
||||||
|
main(opt)
|
||||||
+179
-116
@@ -1,203 +1,256 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
"""
|
||||||
|
YOLO-specific modules
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$ python path/to/models/yolo.py --cfg yolov5s.yaml
|
||||||
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import os
|
||||||
|
import platform
|
||||||
import sys
|
import sys
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
sys.path.append('./') # to run '$ python *.py' files in subdirectories
|
FILE = Path(__file__).resolve()
|
||||||
logger = logging.getLogger(__name__)
|
ROOT = FILE.parents[1] # YOLOv5 root directory
|
||||||
|
if str(ROOT) not in sys.path:
|
||||||
|
sys.path.append(str(ROOT)) # add ROOT to PATH
|
||||||
|
if platform.system() != 'Windows':
|
||||||
|
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
||||||
|
|
||||||
from Y5.models.common import *
|
from models.common import *
|
||||||
from Y5.models.experimental import *
|
from models.experimental import *
|
||||||
from Y5.utils.autoanchor import check_anchor_order
|
from utils.autoanchor import check_anchor_order
|
||||||
from Y5.utils.general import make_divisible, check_file, set_logging
|
from utils.general import LOGGER, check_version, check_yaml, make_divisible, print_args
|
||||||
from Y5.utils.torch_utils import time_synchronized, fuse_conv_and_bn, model_info, scale_img, initialize_weights, \
|
from utils.plots import feature_visualization
|
||||||
select_device, copy_attr
|
from utils.torch_utils import (fuse_conv_and_bn, initialize_weights, model_info, profile, scale_img, select_device,
|
||||||
|
time_sync)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import thop # for FLOPS computation
|
import thop # for FLOPs computation
|
||||||
except ImportError:
|
except ImportError:
|
||||||
thop = None
|
thop = None
|
||||||
|
|
||||||
|
|
||||||
class Detect(nn.Module):
|
class Detect(nn.Module):
|
||||||
stride = None # strides computed during build
|
stride = None # strides computed during build
|
||||||
export = False # onnx export
|
onnx_dynamic = False # ONNX export parameter
|
||||||
|
export = False # export mode
|
||||||
|
|
||||||
def __init__(self, nc=80, anchors=(), ch=()): # detection layer
|
def __init__(self, nc=80, anchors=(), ch=(), inplace=True): # detection layer
|
||||||
super(Detect, self).__init__()
|
super().__init__()
|
||||||
self.nc = nc # number of classes
|
self.nc = nc # number of classes
|
||||||
self.no = nc + 5 # number of outputs per anchor
|
self.no = nc + 5 # number of outputs per anchor
|
||||||
self.nl = len(anchors) # number of detection layers
|
self.nl = len(anchors) # number of detection layers
|
||||||
self.na = len(anchors[0]) // 2 # number of anchors
|
self.na = len(anchors[0]) // 2 # number of anchors
|
||||||
self.grid = [torch.zeros(1)] * self.nl # init grid
|
self.grid = [torch.zeros(1)] * self.nl # init grid
|
||||||
a = torch.tensor(anchors).float().view(self.nl, -1, 2)
|
self.anchor_grid = [torch.zeros(1)] * self.nl # init anchor grid
|
||||||
self.register_buffer('anchors', a) # shape(nl,na,2)
|
self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2)) # shape(nl,na,2)
|
||||||
self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2)
|
|
||||||
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
|
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
|
||||||
|
self.inplace = inplace # use in-place ops (e.g. slice assignment)
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
# x = x.copy() # for profiling
|
|
||||||
z = [] # inference output
|
z = [] # inference output
|
||||||
self.training |= self.export
|
|
||||||
for i in range(self.nl):
|
for i in range(self.nl):
|
||||||
x[i] = self.m[i](x[i]) # conv
|
x[i] = self.m[i](x[i]) # conv
|
||||||
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
|
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
|
||||||
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
|
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
|
||||||
|
|
||||||
if not self.training: # inference
|
if not self.training: # inference
|
||||||
if self.grid[i].shape[2:4] != x[i].shape[2:4]:
|
if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
|
||||||
self.grid[i] = self._make_grid(nx, ny).to(x[i].device)
|
self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
|
||||||
|
|
||||||
y = x[i].sigmoid()
|
y = x[i].sigmoid()
|
||||||
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy
|
if self.inplace:
|
||||||
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
|
y[..., 0:2] = (y[..., 0:2] * 2 + self.grid[i]) * self.stride[i] # xy
|
||||||
|
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
|
||||||
|
else: # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
|
||||||
|
xy, wh, conf = y.split((2, 2, self.nc + 1), 4) # y.tensor_split((2, 4, 5), 4) # torch 1.8.0
|
||||||
|
xy = (xy * 2 + self.grid[i]) * self.stride[i] # xy
|
||||||
|
wh = (wh * 2) ** 2 * self.anchor_grid[i] # wh
|
||||||
|
y = torch.cat((xy, wh, conf), 4)
|
||||||
z.append(y.view(bs, -1, self.no))
|
z.append(y.view(bs, -1, self.no))
|
||||||
|
|
||||||
return x if self.training else (torch.cat(z, 1), x)
|
return x if self.training else (torch.cat(z, 1),) if self.export else (torch.cat(z, 1), x)
|
||||||
|
|
||||||
@staticmethod
|
def _make_grid(self, nx=20, ny=20, i=0):
|
||||||
def _make_grid(nx=20, ny=20):
|
d = self.anchors[i].device
|
||||||
yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
|
t = self.anchors[i].dtype
|
||||||
return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()
|
shape = 1, self.na, ny, nx, 2 # grid shape
|
||||||
|
y, x = torch.arange(ny, device=d, dtype=t), torch.arange(nx, device=d, dtype=t)
|
||||||
|
if check_version(torch.__version__, '1.10.0'): # torch>=1.10.0 meshgrid workaround for torch>=0.7 compatibility
|
||||||
|
yv, xv = torch.meshgrid(y, x, indexing='ij')
|
||||||
|
else:
|
||||||
|
yv, xv = torch.meshgrid(y, x)
|
||||||
|
grid = torch.stack((xv, yv), 2).expand(shape) - 0.5 # add grid offset, i.e. y = 2.0 * x - 0.5
|
||||||
|
anchor_grid = (self.anchors[i] * self.stride[i]).view((1, self.na, 1, 1, 2)).expand(shape)
|
||||||
|
return grid, anchor_grid
|
||||||
|
|
||||||
|
|
||||||
class Model(nn.Module):
|
class Model(nn.Module):
|
||||||
|
# YOLOv5 model
|
||||||
def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None): # model, input channels, number of classes
|
def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None): # model, input channels, number of classes
|
||||||
super(Model, self).__init__()
|
super().__init__()
|
||||||
if isinstance(cfg, dict):
|
if isinstance(cfg, dict):
|
||||||
self.yaml = cfg # model dict
|
self.yaml = cfg # model dict
|
||||||
else: # is *.yaml
|
else: # is *.yaml
|
||||||
import yaml # for torch hub
|
import yaml # for torch hub
|
||||||
self.yaml_file = Path(cfg).name
|
self.yaml_file = Path(cfg).name
|
||||||
with open(cfg) as f:
|
with open(cfg, encoding='ascii', errors='ignore') as f:
|
||||||
self.yaml = yaml.load(f, Loader=yaml.SafeLoader) # model dict
|
self.yaml = yaml.safe_load(f) # model dict
|
||||||
|
|
||||||
# Define model
|
# Define model
|
||||||
ch = self.yaml['ch'] = self.yaml.get('ch', ch) # input channels
|
ch = self.yaml['ch'] = self.yaml.get('ch', ch) # input channels
|
||||||
if nc and nc != self.yaml['nc']:
|
if nc and nc != self.yaml['nc']:
|
||||||
logger.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
|
LOGGER.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
|
||||||
self.yaml['nc'] = nc # override yaml value
|
self.yaml['nc'] = nc # override yaml value
|
||||||
if anchors:
|
if anchors:
|
||||||
logger.info(f'Overriding model.yaml anchors with anchors={anchors}')
|
LOGGER.info(f'Overriding model.yaml anchors with anchors={anchors}')
|
||||||
self.yaml['anchors'] = round(anchors) # override yaml value
|
self.yaml['anchors'] = round(anchors) # override yaml value
|
||||||
self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist
|
self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist
|
||||||
self.names = [str(i) for i in range(self.yaml['nc'])] # default names
|
self.names = [str(i) for i in range(self.yaml['nc'])] # default names
|
||||||
# print([x.shape for x in self.forward(torch.zeros(1, ch, 64, 64))])
|
self.inplace = self.yaml.get('inplace', True)
|
||||||
|
|
||||||
# Build strides, anchors
|
# Build strides, anchors
|
||||||
m = self.model[-1] # Detect()
|
m = self.model[-1] # Detect()
|
||||||
if isinstance(m, Detect):
|
if isinstance(m, Detect):
|
||||||
s = 256 # 2x min stride
|
s = 256 # 2x min stride
|
||||||
|
m.inplace = self.inplace
|
||||||
m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward
|
m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward
|
||||||
|
check_anchor_order(m) # must be in pixel-space (not grid-space)
|
||||||
m.anchors /= m.stride.view(-1, 1, 1)
|
m.anchors /= m.stride.view(-1, 1, 1)
|
||||||
check_anchor_order(m)
|
|
||||||
self.stride = m.stride
|
self.stride = m.stride
|
||||||
self._initialize_biases() # only run once
|
self._initialize_biases() # only run once
|
||||||
# print('Strides: %s' % m.stride.tolist())
|
|
||||||
|
|
||||||
# Init weights, biases
|
# Init weights, biases
|
||||||
initialize_weights(self)
|
initialize_weights(self)
|
||||||
self.info()
|
self.info()
|
||||||
logger.info('')
|
LOGGER.info('')
|
||||||
|
|
||||||
def forward(self, x, augment=False, profile=False):
|
def forward(self, x, augment=False, profile=False, visualize=False):
|
||||||
if augment:
|
if augment:
|
||||||
img_size = x.shape[-2:] # height, width
|
return self._forward_augment(x) # augmented inference, None
|
||||||
s = [1, 0.83, 0.67] # scales
|
return self._forward_once(x, profile, visualize) # single-scale inference, train
|
||||||
f = [None, 3, None] # flips (2-ud, 3-lr)
|
|
||||||
y = [] # outputs
|
|
||||||
for si, fi in zip(s, f):
|
|
||||||
xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))
|
|
||||||
yi = self.forward_once(xi)[0] # forward
|
|
||||||
# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save
|
|
||||||
yi[..., :4] /= si # de-scale
|
|
||||||
if fi == 2:
|
|
||||||
yi[..., 1] = img_size[0] - yi[..., 1] # de-flip ud
|
|
||||||
elif fi == 3:
|
|
||||||
yi[..., 0] = img_size[1] - yi[..., 0] # de-flip lr
|
|
||||||
y.append(yi)
|
|
||||||
return torch.cat(y, 1), None # augmented inference, train
|
|
||||||
else:
|
|
||||||
return self.forward_once(x, profile) # single-scale inference, train
|
|
||||||
|
|
||||||
def forward_once(self, x, profile=False):
|
def _forward_augment(self, x):
|
||||||
|
img_size = x.shape[-2:] # height, width
|
||||||
|
s = [1, 0.83, 0.67] # scales
|
||||||
|
f = [None, 3, None] # flips (2-ud, 3-lr)
|
||||||
|
y = [] # outputs
|
||||||
|
for si, fi in zip(s, f):
|
||||||
|
xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))
|
||||||
|
yi = self._forward_once(xi)[0] # forward
|
||||||
|
# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save
|
||||||
|
yi = self._descale_pred(yi, fi, si, img_size)
|
||||||
|
y.append(yi)
|
||||||
|
y = self._clip_augmented(y) # clip augmented tails
|
||||||
|
return torch.cat(y, 1), None # augmented inference, train
|
||||||
|
|
||||||
|
def _forward_once(self, x, profile=False, visualize=False):
|
||||||
y, dt = [], [] # outputs
|
y, dt = [], [] # outputs
|
||||||
for m in self.model:
|
for m in self.model:
|
||||||
if m.f != -1: # if not from previous layer
|
if m.f != -1: # if not from previous layer
|
||||||
x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
|
x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
|
||||||
|
|
||||||
if profile:
|
if profile:
|
||||||
o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPS
|
self._profile_one_layer(m, x, dt)
|
||||||
t = time_synchronized()
|
|
||||||
for _ in range(10):
|
|
||||||
_ = m(x)
|
|
||||||
dt.append((time_synchronized() - t) * 100)
|
|
||||||
print('%10.1f%10.0f%10.1fms %-40s' % (o, m.np, dt[-1], m.type))
|
|
||||||
|
|
||||||
x = m(x) # run
|
x = m(x) # run
|
||||||
y.append(x if m.i in self.save else None) # save output
|
y.append(x if m.i in self.save else None) # save output
|
||||||
|
if visualize:
|
||||||
if profile:
|
feature_visualization(x, m.type, m.i, save_dir=visualize)
|
||||||
print('%.1fms total' % sum(dt))
|
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
def _descale_pred(self, p, flips, scale, img_size):
|
||||||
|
# de-scale predictions following augmented inference (inverse operation)
|
||||||
|
if self.inplace:
|
||||||
|
p[..., :4] /= scale # de-scale
|
||||||
|
if flips == 2:
|
||||||
|
p[..., 1] = img_size[0] - p[..., 1] # de-flip ud
|
||||||
|
elif flips == 3:
|
||||||
|
p[..., 0] = img_size[1] - p[..., 0] # de-flip lr
|
||||||
|
else:
|
||||||
|
x, y, wh = p[..., 0:1] / scale, p[..., 1:2] / scale, p[..., 2:4] / scale # de-scale
|
||||||
|
if flips == 2:
|
||||||
|
y = img_size[0] - y # de-flip ud
|
||||||
|
elif flips == 3:
|
||||||
|
x = img_size[1] - x # de-flip lr
|
||||||
|
p = torch.cat((x, y, wh, p[..., 4:]), -1)
|
||||||
|
return p
|
||||||
|
|
||||||
|
def _clip_augmented(self, y):
|
||||||
|
# Clip YOLOv5 augmented inference tails
|
||||||
|
nl = self.model[-1].nl # number of detection layers (P3-P5)
|
||||||
|
g = sum(4 ** x for x in range(nl)) # grid points
|
||||||
|
e = 1 # exclude layer count
|
||||||
|
i = (y[0].shape[1] // g) * sum(4 ** x for x in range(e)) # indices
|
||||||
|
y[0] = y[0][:, :-i] # large
|
||||||
|
i = (y[-1].shape[1] // g) * sum(4 ** (nl - 1 - x) for x in range(e)) # indices
|
||||||
|
y[-1] = y[-1][:, i:] # small
|
||||||
|
return y
|
||||||
|
|
||||||
|
def _profile_one_layer(self, m, x, dt):
|
||||||
|
c = isinstance(m, Detect) # is final layer, copy input as inplace fix
|
||||||
|
o = thop.profile(m, inputs=(x.copy() if c else x,), verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPs
|
||||||
|
t = time_sync()
|
||||||
|
for _ in range(10):
|
||||||
|
m(x.copy() if c else x)
|
||||||
|
dt.append((time_sync() - t) * 100)
|
||||||
|
if m == self.model[0]:
|
||||||
|
LOGGER.info(f"{'time (ms)':>10s} {'GFLOPs':>10s} {'params':>10s} module")
|
||||||
|
LOGGER.info(f'{dt[-1]:10.2f} {o:10.2f} {m.np:10.0f} {m.type}')
|
||||||
|
if c:
|
||||||
|
LOGGER.info(f"{sum(dt):10.2f} {'-':>10s} {'-':>10s} Total")
|
||||||
|
|
||||||
def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency
|
def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency
|
||||||
# https://arxiv.org/abs/1708.02002 section 3.3
|
# https://arxiv.org/abs/1708.02002 section 3.3
|
||||||
# cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1.
|
# cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1.
|
||||||
m = self.model[-1] # Detect() module
|
m = self.model[-1] # Detect() module
|
||||||
for mi, s in zip(m.m, m.stride): # from
|
for mi, s in zip(m.m, m.stride): # from
|
||||||
b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85)
|
b = mi.bias.view(m.na, -1).detach() # conv.bias(255) to (3,85)
|
||||||
b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image)
|
b[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image)
|
||||||
b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls
|
b[:, 5:] += math.log(0.6 / (m.nc - 0.999999)) if cf is None else torch.log(cf / cf.sum()) # cls
|
||||||
mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True)
|
mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True)
|
||||||
|
|
||||||
def _print_biases(self):
|
def _print_biases(self):
|
||||||
m = self.model[-1] # Detect() module
|
m = self.model[-1] # Detect() module
|
||||||
for mi in m.m: # from
|
for mi in m.m: # from
|
||||||
b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85)
|
b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85)
|
||||||
print(('%6g Conv2d.bias:' + '%10.3g' * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean()))
|
LOGGER.info(
|
||||||
|
('%6g Conv2d.bias:' + '%10.3g' * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean()))
|
||||||
|
|
||||||
# def _print_weights(self):
|
# def _print_weights(self):
|
||||||
# for m in self.model.modules():
|
# for m in self.model.modules():
|
||||||
# if type(m) is Bottleneck:
|
# if type(m) is Bottleneck:
|
||||||
# print('%10.3g' % (m.w.detach().sigmoid() * 2)) # shortcut weights
|
# LOGGER.info('%10.3g' % (m.w.detach().sigmoid() * 2)) # shortcut weights
|
||||||
|
|
||||||
def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers
|
def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers
|
||||||
#print('Fusing layers... ')
|
LOGGER.info('Fusing layers... ')
|
||||||
for m in self.model.modules():
|
for m in self.model.modules():
|
||||||
if type(m) is Conv and hasattr(m, 'bn'):
|
if isinstance(m, (Conv, DWConv)) and hasattr(m, 'bn'):
|
||||||
m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv
|
m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv
|
||||||
delattr(m, 'bn') # remove batchnorm
|
delattr(m, 'bn') # remove batchnorm
|
||||||
m.forward = m.fuseforward # update forward
|
m.forward = m.forward_fuse # update forward
|
||||||
self.info()
|
self.info()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def nms(self, mode=True): # add or remove NMS module
|
|
||||||
present = type(self.model[-1]) is NMS # last layer is NMS
|
|
||||||
if mode and not present:
|
|
||||||
print('Adding NMS... ')
|
|
||||||
m = NMS() # module
|
|
||||||
m.f = -1 # from
|
|
||||||
m.i = self.model[-1].i + 1 # index
|
|
||||||
self.model.add_module(name='%s' % m.i, module=m) # add
|
|
||||||
self.eval()
|
|
||||||
elif not mode and present:
|
|
||||||
print('Removing NMS... ')
|
|
||||||
self.model = self.model[:-1] # remove
|
|
||||||
return self
|
|
||||||
|
|
||||||
def autoshape(self): # add autoShape module
|
|
||||||
print('Adding autoShape... ')
|
|
||||||
m = autoShape(self) # wrap model
|
|
||||||
copy_attr(m, self, include=('yaml', 'nc', 'hyp', 'names', 'stride'), exclude=()) # copy attributes
|
|
||||||
return m
|
|
||||||
|
|
||||||
def info(self, verbose=False, img_size=640): # print model information
|
def info(self, verbose=False, img_size=640): # print model information
|
||||||
model_info(self, verbose, img_size)
|
model_info(self, verbose, img_size)
|
||||||
|
|
||||||
|
def _apply(self, fn):
|
||||||
|
# Apply to(), cpu(), cuda(), half() to model tensors that are not parameters or registered buffers
|
||||||
|
self = super()._apply(fn)
|
||||||
|
m = self.model[-1] # Detect()
|
||||||
|
if isinstance(m, Detect):
|
||||||
|
m.stride = fn(m.stride)
|
||||||
|
m.grid = list(map(fn, m.grid))
|
||||||
|
if isinstance(m.anchor_grid, list):
|
||||||
|
m.anchor_grid = list(map(fn, m.anchor_grid))
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
def parse_model(d, ch): # model_dict, input_channels(3)
|
def parse_model(d, ch): # model_dict, input_channels(3)
|
||||||
logger.info('\n%3s%18s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments'))
|
LOGGER.info(f"\n{'':>3}{'from':>18}{'n':>3}{'params':>10} {'module':<40}{'arguments':<30}")
|
||||||
anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
|
anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
|
||||||
na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors
|
na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors
|
||||||
no = na * (nc + 5) # number of outputs = anchors * (classes + 5)
|
no = na * (nc + 5) # number of outputs = anchors * (classes + 5)
|
||||||
@@ -208,24 +261,24 @@ def parse_model(d, ch): # model_dict, input_channels(3)
|
|||||||
for j, a in enumerate(args):
|
for j, a in enumerate(args):
|
||||||
try:
|
try:
|
||||||
args[j] = eval(a) if isinstance(a, str) else a # eval strings
|
args[j] = eval(a) if isinstance(a, str) else a # eval strings
|
||||||
except:
|
except NameError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
n = max(round(n * gd), 1) if n > 1 else n # depth gain
|
n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain
|
||||||
if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
|
if m in (Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, MixConv2d, Focus, CrossConv,
|
||||||
C3]:
|
BottleneckCSP, C3, C3TR, C3SPP, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x):
|
||||||
c1, c2 = ch[f], args[0]
|
c1, c2 = ch[f], args[0]
|
||||||
if c2 != no: # if not output
|
if c2 != no: # if not output
|
||||||
c2 = make_divisible(c2 * gw, 8)
|
c2 = make_divisible(c2 * gw, 8)
|
||||||
|
|
||||||
args = [c1, c2, *args[1:]]
|
args = [c1, c2, *args[1:]]
|
||||||
if m in [BottleneckCSP, C3]:
|
if m in [BottleneckCSP, C3, C3TR, C3Ghost, C3x]:
|
||||||
args.insert(2, n) # number of repeats
|
args.insert(2, n) # number of repeats
|
||||||
n = 1
|
n = 1
|
||||||
elif m is nn.BatchNorm2d:
|
elif m is nn.BatchNorm2d:
|
||||||
args = [ch[f]]
|
args = [ch[f]]
|
||||||
elif m is Concat:
|
elif m is Concat:
|
||||||
c2 = sum([ch[x] for x in f])
|
c2 = sum(ch[x] for x in f)
|
||||||
elif m is Detect:
|
elif m is Detect:
|
||||||
args.append([ch[x] for x in f])
|
args.append([ch[x] for x in f])
|
||||||
if isinstance(args[1], int): # number of anchors
|
if isinstance(args[1], int): # number of anchors
|
||||||
@@ -237,11 +290,11 @@ def parse_model(d, ch): # model_dict, input_channels(3)
|
|||||||
else:
|
else:
|
||||||
c2 = ch[f]
|
c2 = ch[f]
|
||||||
|
|
||||||
m_ = nn.Sequential(*[m(*args) for _ in range(n)]) if n > 1 else m(*args) # module
|
m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module
|
||||||
t = str(m)[8:-2].replace('__main__.', '') # module type
|
t = str(m)[8:-2].replace('__main__.', '') # module type
|
||||||
np = sum([x.numel() for x in m_.parameters()]) # number params
|
np = sum(x.numel() for x in m_.parameters()) # number params
|
||||||
m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params
|
m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params
|
||||||
logger.info('%3s%18s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print
|
LOGGER.info(f'{i:>3}{str(f):>18}{n_:>3}{np:10.0f} {t:<40}{str(args):<30}') # print
|
||||||
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
|
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
|
||||||
layers.append(m_)
|
layers.append(m_)
|
||||||
if i == 0:
|
if i == 0:
|
||||||
@@ -253,23 +306,33 @@ def parse_model(d, ch): # model_dict, input_channels(3)
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--cfg', type=str, default='yolov5s.yaml', help='model.yaml')
|
parser.add_argument('--cfg', type=str, default='yolov5s.yaml', help='model.yaml')
|
||||||
|
parser.add_argument('--batch-size', type=int, default=1, help='total batch size for all GPUs')
|
||||||
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
||||||
|
parser.add_argument('--profile', action='store_true', help='profile model speed')
|
||||||
|
parser.add_argument('--line-profile', action='store_true', help='profile model speed layer by layer')
|
||||||
|
parser.add_argument('--test', action='store_true', help='test all yolo*.yaml')
|
||||||
opt = parser.parse_args()
|
opt = parser.parse_args()
|
||||||
opt.cfg = check_file(opt.cfg) # check file
|
opt.cfg = check_yaml(opt.cfg) # check YAML
|
||||||
set_logging()
|
print_args(vars(opt))
|
||||||
device = select_device(opt.device)
|
device = select_device(opt.device)
|
||||||
|
|
||||||
# Create model
|
# Create model
|
||||||
|
im = torch.rand(opt.batch_size, 3, 640, 640).to(device)
|
||||||
model = Model(opt.cfg).to(device)
|
model = Model(opt.cfg).to(device)
|
||||||
model.train()
|
|
||||||
|
|
||||||
# Profile
|
# Options
|
||||||
# img = torch.rand(8 if torch.cuda.is_available() else 1, 3, 640, 640).to(device)
|
if opt.line_profile: # profile layer by layer
|
||||||
# y = model(img, profile=True)
|
_ = model(im, profile=True)
|
||||||
|
|
||||||
# Tensorboard
|
elif opt.profile: # profile forward-backward
|
||||||
# from torch.utils.tensorboard import SummaryWriter
|
results = profile(input=im, ops=[model], n=3)
|
||||||
# tb_writer = SummaryWriter()
|
|
||||||
# print("Run 'tensorboard --logdir=models/runs' to view tensorboard at http://localhost:6006/")
|
elif opt.test: # test all models
|
||||||
# tb_writer.add_graph(model.model, img) # add model to tensorboard
|
for cfg in Path(ROOT / 'models').rglob('yolo*.yaml'):
|
||||||
# tb_writer.add_image('test', img[0], dataformats='CWH') # add model to tensorboard
|
try:
|
||||||
|
_ = Model(cfg)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Error in {cfg}: {e}')
|
||||||
|
|
||||||
|
else: # report fused model summary
|
||||||
|
model.fuse()
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.0 # model depth multiple
|
depth_multiple: 1.0 # model depth multiple
|
||||||
width_multiple: 1.0 # layer channel multiple
|
width_multiple: 1.0 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [116,90, 156,198, 373,326] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[-1, 3, C3, [128]],
|
[-1, 3, C3, [128]],
|
||||||
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[-1, 9, C3, [256]],
|
[-1, 6, C3, [256]],
|
||||||
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[-1, 9, C3, [512]],
|
[-1, 9, C3, [512]],
|
||||||
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
[-1, 3, C3, [1024]],
|
||||||
[-1, 3, C3, [1024, False]], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[[-1, 1, Conv, [512, 1, 1]],
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 0.67 # model depth multiple
|
depth_multiple: 0.67 # model depth multiple
|
||||||
width_multiple: 0.75 # layer channel multiple
|
width_multiple: 0.75 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [116,90, 156,198, 373,326] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[-1, 3, C3, [128]],
|
[-1, 3, C3, [128]],
|
||||||
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[-1, 9, C3, [256]],
|
[-1, 6, C3, [256]],
|
||||||
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[-1, 9, C3, [512]],
|
[-1, 9, C3, [512]],
|
||||||
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
[-1, 3, C3, [1024]],
|
||||||
[-1, 3, C3, [1024, False]], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[[-1, 1, Conv, [512, 1, 1]],
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
|||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
nc: 80 # number of classes
|
||||||
|
depth_multiple: 0.33 # model depth multiple
|
||||||
|
width_multiple: 0.25 # layer channel multiple
|
||||||
|
anchors:
|
||||||
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 backbone
|
||||||
|
backbone:
|
||||||
|
# [from, number, module, args]
|
||||||
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
|
[-1, 3, C3, [128]],
|
||||||
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
|
[-1, 6, C3, [256]],
|
||||||
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
|
[-1, 9, C3, [512]],
|
||||||
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
|
[-1, 3, C3, [1024]],
|
||||||
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
|
]
|
||||||
|
|
||||||
|
# YOLOv5 v6.0 head
|
||||||
|
head:
|
||||||
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 6], 1, Concat, [1]], # cat backbone P4
|
||||||
|
[-1, 3, C3, [512, False]], # 13
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 1, 1]],
|
||||||
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
[[-1, 4], 1, Concat, [1]], # cat backbone P3
|
||||||
|
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [256, 3, 2]],
|
||||||
|
[[-1, 14], 1, Concat, [1]], # cat head P4
|
||||||
|
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
|
||||||
|
|
||||||
|
[-1, 1, Conv, [512, 3, 2]],
|
||||||
|
[[-1, 10], 1, Concat, [1]], # cat head P5
|
||||||
|
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
|
||||||
|
|
||||||
|
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
|
||||||
|
]
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 0.33 # model depth multiple
|
depth_multiple: 0.33 # model depth multiple
|
||||||
width_multiple: 0.50 # layer channel multiple
|
width_multiple: 0.50 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [116,90, 156,198, 373,326] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[-1, 3, C3, [128]],
|
[-1, 3, C3, [128]],
|
||||||
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[-1, 9, C3, [256]],
|
[-1, 6, C3, [256]],
|
||||||
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[-1, 9, C3, [512]],
|
[-1, 9, C3, [512]],
|
||||||
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
[-1, 3, C3, [1024]],
|
||||||
[-1, 3, C3, [1024, False]], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[[-1, 1, Conv, [512, 1, 1]],
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
# parameters
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
|
||||||
|
# Parameters
|
||||||
nc: 80 # number of classes
|
nc: 80 # number of classes
|
||||||
depth_multiple: 1.33 # model depth multiple
|
depth_multiple: 1.33 # model depth multiple
|
||||||
width_multiple: 1.25 # layer channel multiple
|
width_multiple: 1.25 # layer channel multiple
|
||||||
|
|
||||||
# anchors
|
|
||||||
anchors:
|
anchors:
|
||||||
- [10,13, 16,30, 33,23] # P3/8
|
- [10,13, 16,30, 33,23] # P3/8
|
||||||
- [30,61, 62,45, 59,119] # P4/16
|
- [30,61, 62,45, 59,119] # P4/16
|
||||||
- [116,90, 156,198, 373,326] # P5/32
|
- [116,90, 156,198, 373,326] # P5/32
|
||||||
|
|
||||||
# YOLOv5 backbone
|
# YOLOv5 v6.0 backbone
|
||||||
backbone:
|
backbone:
|
||||||
# [from, number, module, args]
|
# [from, number, module, args]
|
||||||
[[-1, 1, Focus, [64, 3]], # 0-P1/2
|
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
|
||||||
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
|
||||||
[-1, 3, C3, [128]],
|
[-1, 3, C3, [128]],
|
||||||
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
|
||||||
[-1, 9, C3, [256]],
|
[-1, 6, C3, [256]],
|
||||||
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
|
||||||
[-1, 9, C3, [512]],
|
[-1, 9, C3, [512]],
|
||||||
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
|
||||||
[-1, 1, SPP, [1024, [5, 9, 13]]],
|
[-1, 3, C3, [1024]],
|
||||||
[-1, 3, C3, [1024, False]], # 9
|
[-1, 1, SPPF, [1024, 5]], # 9
|
||||||
]
|
]
|
||||||
|
|
||||||
# YOLOv5 head
|
# YOLOv5 v6.0 head
|
||||||
head:
|
head:
|
||||||
[[-1, 1, Conv, [512, 1, 1]],
|
[[-1, 1, Conv, [512, 1, 1]],
|
||||||
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
||||||
|
"""
|
||||||
|
utils/initialization
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def notebook_init(verbose=True):
|
||||||
|
# Check system software and hardware
|
||||||
|
print('Checking setup...')
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from utils.general import check_requirements, emojis, is_colab
|
||||||
|
from utils.torch_utils import select_device # imports
|
||||||
|
|
||||||
|
check_requirements(('psutil', 'IPython'))
|
||||||
|
import psutil
|
||||||
|
from IPython import display # to display images and clear console output
|
||||||
|
|
||||||
|
if is_colab():
|
||||||
|
shutil.rmtree('/content/sample_data', ignore_errors=True) # remove colab /sample_data directory
|
||||||
|
|
||||||
|
# System info
|
||||||
|
if verbose:
|
||||||
|
gb = 1 << 30 # bytes to GiB (1024 ** 3)
|
||||||
|
ram = psutil.virtual_memory().total
|
||||||
|
total, used, free = shutil.disk_usage("/")
|
||||||
|
display.clear_output()
|
||||||
|
s = f'({os.cpu_count()} CPUs, {ram / gb:.1f} GB RAM, {(total - free) / gb:.1f}/{total / gb:.1f} GB disk)'
|
||||||
|
else:
|
||||||
|
s = ''
|
||||||
|
|
||||||
|
select_device(newline=False)
|
||||||
|
print(emojis(f'Setup complete ✅ {s}'))
|
||||||
|
return display
|
||||||
Regular → Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Loaded 100 of 513 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user